DOM1級(jí)主要是在定義HTML和XML文檔的低層結(jié)構(gòu)。D2和D3則在這個(gè)結(jié)構(gòu)的基礎(chǔ)上引入了更多的交互能力。它們被分為了許多模塊:
- DOM Level 2 Core:為1級(jí)核心添加了更多方法和屬性
- DOM Level 2 Views:為文檔定義了基于樣式信息的不同視圖
- DOM Level 2 Events:說(shuō)明了如何使用事件與DOM文檔交互
- DOM Level 2 Style:有關(guān)CSS
- DOM Level 2 Traversal and Range:遍歷和選擇的新API
- DOM Level 2 HTML:添加了新方法和屬性的HTML
DOM變化
DOM2級(jí)核心沒(méi)有引進(jìn)新類型,增強(qiáng)了既有類型。DOM3級(jí)核心既引進(jìn)了新類型又增強(qiáng)了既有類型。
DOM Level 2 Views、DOM Level 2 HTML也提供了新的屬性和方法。
重點(diǎn)之一是對(duì)命名空間的支持。
看瀏覽器兼不兼容:
var supportsDOM2Core = document.implementation.hasFeature("Core", "2.0");
var supportsDOM3Core = document.implementation.hasFeature("Core", "3.0");
var supportsDOM2HTML = document.implementation.hasFeature("HTML", "2.0");
var supportsDOM2Views = document.implementation.hasFeature("Views", "2.0");
var supportsDOM2XML = document.implementation.hasFeature("XML", "2.0");
針對(duì) XML命名空間的變化
有了XML命名空間,不同XML文檔的元素就可以混合在一起。技術(shù)上說(shuō)HTML不支持XML命名空間,但JSP,XHTML支持。
命名空間使用xmlns特性來(lái)指定。XHTML的命名空間是http://www.w3.org/1999/xhtml。
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example XHTML page</title>
</head>
<body>
Hello world!
</body>
</html>
在上面的例子中,所有元素都默認(rèn)為XHTML命名空間的元素。想要明確的指定那些元素屬于這個(gè)命名空間就要使用前綴:
<xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml">
<xhtml:head>
<xhtml:title>Example XHTML page</xhtml:title>
</xhtml:head>
<xhtml:body xhtml:class="home">
Hello world!
</xhtml:body>
</xhtml:html>
這時(shí)所有的XHTML元素的前綴都要是這個(gè)才行,有時(shí)為了避免沖突,也需要用命名空間來(lái)限定特性。這個(gè)在使用單一語(yǔ)言來(lái)編寫XML文檔時(shí)沒(méi)啥用,但是在多語(yǔ)言時(shí)就有用了:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example XHTML page</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100" style="width:100%; height:100%">
<rect x="0" y="0" width="100" height="100" style="fill:red"/>
</svg>
</body>
</html>
或
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example XHTML page</title>
</head>
<body>
<s:svg xmlns:s="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100" style="width:100%; height:100%">
<s:rect x="0" y="0" width="100" height="100" style="fill:red"/>
</s:svg>
</body>
</html>
這個(gè)例子中通過(guò)設(shè)置命名空間,將svg標(biāo)識(shí)為了與包含文檔無(wú)關(guān)的元素。此時(shí)svg元素的所有子元素以及這些元素的所有特性都屬于了http://www.w3.org/2000/svg。所以即便這是一個(gè)XHTML文檔,其中的svg代碼還是有效的。
DOM2同時(shí)提供了相應(yīng)的查詢和創(chuàng)建有命名空間歸屬的節(jié)點(diǎn)版本的方法。
Node類型的變化
在DOM2級(jí)中,node類型包含下列特定于命名空間的屬性。
- localName:不帶命名空間前綴的節(jié)點(diǎn)名稱
- namespaceURI:命名空間URI
- prefix:命名空間前綴
DOM3級(jí)中有如下方法: - isDefaultNamespace(namespaceURI):看看當(dāng)前節(jié)點(diǎn)的默認(rèn)命名空間是不是傳入的參數(shù)
- lookupNamespaceURI(prefix):返回給定前綴的命名空間
- lookupPrefix(namespaceURI):返回給定URI的前綴
這里有個(gè)神奇的事情:
//這里比較有趣,svg節(jié)點(diǎn)的defaultNameSpace不是我們給他設(shè)置的那個(gè)而是上一級(jí)的。
alert(svg.isDefaultNamespace("http://www.w3.org/1999/xhtml")); //true
alert(svg.isDefaultNamespace("http://www.w3.org/2000/svg")); //false
Document類型的變化
createElementNS(namespaceURI, tagName)
createAttributeNS(namespaceURI, attributeName)
getElementsByTagNameNS(namespaceURI, tagName)
Element類型的變化
getAttributeNS(namespaceURI,localName)
getAttributeNodeNS(namespaceURI,localName)
getElementsByTagNameNS(namespaceURI, tagName)
hasAttributeNS(namespaceURI,localName)
removeAttriubteNS(namespaceURI,localName)
setAttributeNS(namespaceURI,qualifiedName,value)
setAttributeNodeNS(attNode)
NamedNodeMap 類型的變化
getNamedItemNS(namespaceURI,localName)
removeNamedItemNS(namespaceURI,localName)
setNamedItemNS(node)
其他方面變化
DocumentType
新增3個(gè)屬性publicId、systemId、internalSubset
Document類型的變化
importNode()
這個(gè)方法用于導(dǎo)入一個(gè)來(lái)自其他文檔的節(jié)點(diǎn)appendChild()如果加入一個(gè)來(lái)自其他文檔的節(jié)點(diǎn)會(huì)報(bào)錯(cuò)。這個(gè)方法會(huì)把別的文檔的節(jié)點(diǎn)轉(zhuǎn)化成本文檔的。這個(gè)方法有點(diǎn)像cloneCode(),都是接收一個(gè)節(jié)點(diǎn)和一個(gè)布爾值,返回淺復(fù)制或深復(fù)制的節(jié)點(diǎn),只不過(guò)這個(gè)節(jié)點(diǎn)的ownerDocument會(huì)被重置。
var newNode = document.importNode(oldNode, true);
document.body.appendChild(newNode);
defaultView
這個(gè)指針指向擁有給定文檔的窗口或框架,View。IE不支持,I使用parentWindow,所以要是想判斷文檔歸屬的窗口:
var parentWindow = document.defaultView || document.parentWindow;
createDocumentType()、createDocument()
document.implementation的方法,Core。創(chuàng)建一個(gè)DocumentType、創(chuàng)建一個(gè)新文檔
//新建一個(gè)XHTML文檔
var doctype = document.implementation.createDocumentType("html",
" -//W3C//DTD XHTML 1.0 Strict//EN",
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
var doc = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html", doctype);
createHTMLDocument()
用來(lái)創(chuàng)建一個(gè)完整的HTML文檔,包括html,head,body,title。接收的字符串參數(shù)會(huì)放到title里。HTML
Node類型的變化
isSupported()
當(dāng)前節(jié)點(diǎn)具有的能力
if (document.body.isSupported("HTML", "2.0")){
}
isSameNode()、isEqualNode()
DOM3
isSameNode()代表兩個(gè)節(jié)點(diǎn)引用同一個(gè)對(duì)象。
isEqualNode()代表兩個(gè)節(jié)點(diǎn)類型相同,屬性相同,屬性值相等。
//相等,不相同的兩個(gè)節(jié)點(diǎn)
var div1 = document.createElement("div");
div1.setAttribute("class", "box");
var div2 = document.createElement("div");
div2.setAttribute("class", "box");
alert(div1.isSameNode(div1)); //true
alert(div1.isEqualNode(div2)); //true
alert(div1.isSameNode(div2)); //false
setUserData()
這個(gè)方法比較神奇,可以為節(jié)點(diǎn)額外添加數(shù)據(jù),3個(gè)參數(shù):鍵,值,處理函數(shù)。這個(gè)處理函數(shù)會(huì)在節(jié)點(diǎn)被復(fù)制,導(dǎo)入新文檔,刪除,重命名時(shí)被調(diào)用。這個(gè)函數(shù)接受5個(gè)參數(shù):操作類型(1、2、3、4),數(shù)據(jù)鍵、數(shù)據(jù)值、源節(jié)點(diǎn)、目標(biāo)節(jié)點(diǎn)。
/***********************Node類型的新方法:setUserData(),不過(guò)Safari和chrome都不支持貌似********/
var div = document.createElement("div");
div.setUserData("name", "Nicholas", function(operation, key, value, src, dest){
if (operation == 1){
dest.setUserData(key, value, function(){});
}
});
var newDiv = div.cloneNode(true);
alert(newDiv.getUserData("name")); //"Nicholas"
框架的變化
框架和內(nèi)嵌框架HTMLFrameElement、HTMLIFrameElement。這兩個(gè)類型有新屬性contentDocument。指向表示框架內(nèi)容的文檔對(duì)象。
在此之前無(wú)法通過(guò)元素獲得這個(gè)對(duì)象,這個(gè)對(duì)象是Document類型的。IE8之前不支持,可以使用contentWindow。contentWindow所有瀏覽器都支持。
var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
樣式
要支持DOM2級(jí)CSS
var supportsDOM2CSS = document.implementation.hasFeature("CSS", "2.0");
var supportsDOM2CSS2 = document.implementation.hasFeature("CSS2", "2.0");
訪問(wèn)元素的樣式
任何支持style特性的HTML元素在JS中都有一個(gè)對(duì)應(yīng)的style屬性。它是CSSStyleDeclaration的實(shí)例,這里包含通過(guò)HTML的style特性指定的所有樣式信息,但不包含外部與內(nèi)嵌樣式表的樣式。樣式通過(guò)屬性訪問(wèn),駝峰命名。
var myDiv = document.getElementById("myDiv");
myDiv.style.backgroundColor = "red";
myDiv.style.width = "100px";
myDiv.style.height = "200px";
myDiv.style.border = "1px solid black";
DOM樣式屬性和方法
屬性和方法用來(lái)訪問(wèn)和修改樣式。
- ? cssText:訪問(wèn)CSS中的特性代碼,設(shè)置時(shí)覆蓋原來(lái)的。
- length:CSS屬性的數(shù)量
- parentRule:CSSRule對(duì)象
- getPropertyCSSValue(propertyName):給定屬性的CSSValue
- getPropertyPriority(propertyName):如果給定屬性有!important則返回"important",否則空字符串。
- getPropertyValue(propertyName):給定屬性值 ?
- item(index):給定位置的CSS屬性名稱
- removeProperty(propertyName):從樣式表中刪除給定屬性
- setProperty(propertyName,value,priority):為給定屬性設(shè)置值和優(yōu)先級(jí)。
var prop, value, i, len;
for (i=0, len=myDiv.style.length; i < len; i++){
prop = myDiv.style[i]; //myDiv.style.item(i)
value = myDiv.style.getPropertyValue(prop);
alert(prop + " : " + value);
}
計(jì)算的樣式
style只包含直接寫在HTML里的特性,但是不支持樣式表的。這是個(gè)大問(wèn)題。
document.defaultView.getComputedStyle()
這個(gè)方法接收兩個(gè)參數(shù),要取得計(jì)算樣式的元素和一個(gè)偽元素字符串。返回一個(gè)CSSStyleDeclaration。包含所有計(jì)算后的屬性。
IE不支持,但是IE每個(gè)節(jié)點(diǎn)都有一個(gè)currentStyle屬性,這里包含了計(jì)算后的屬性。
計(jì)算后樣式只讀。
var myDiv = document.getElementById("myDiv");
var computedStyle = document.defaultView.getComputedStyle(myDiv, null);
//IE不支持,使用currentStyle
//var computedStyle = myDiv.currentStyle;
alert(computedStyle.height); // "200px"
操作樣式表
CSSStyleSheet類型表示樣式表,繼承自StyleSheet,后者可以作為一個(gè)基礎(chǔ)接口來(lái)定義非CSS樣式表。CSSStyleSheet繼承了如下屬性。
- disabled:樣式表是否被禁用
- href:樣式表的URL
- media:樣式表支持的媒體類型集合
- ownerNode:指向擁有當(dāng)前樣式表節(jié)點(diǎn)的指針,IE不支持
- parentStyleSheet:當(dāng)前樣式表通過(guò)@import導(dǎo)入的情況下,這個(gè)屬性指向?qū)胨臉邮奖?/li>
- title:ownerNode中title的值
- type:表示樣式表類型的字符串
- cssRules:樣式表中包含的樣式規(guī)則的集合,IE使用rules
- ownerRule:當(dāng)前樣式表通過(guò)@import導(dǎo)入的情況下,指向表示導(dǎo)入的規(guī)則,IE不支持
- deleteRule(index):刪除cssRules集合中指定位置的規(guī)則,IE使用removeRule()
- insertRule(rule,index):向cssRules集合中指定位置插入rule字符串,IE使用addRule()
應(yīng)用于文檔的所有樣式通過(guò)document.styleSheets集合來(lái)表示。
var sheet = null;
for (var i=0, len=document.styleSheets.length; i < len; i++){
sheet = document.styleSheets[i];
alert(sheet.href);
}
直接通過(guò)link,style元素取得CSSStyleSheet。
function getStyleSheet(element){
return element.sheet || element.styleSheet;
}
var link = document.getElementsByTagName("link")[0];
var sheet = getStyleSheet(link);
alert(sheet.href);
CSSRule對(duì)象
這個(gè)對(duì)象表示樣式表中的每一條規(guī)則。它是一個(gè)基類,不止是CSS規(guī)則,包括@import、@font-face、@page、@charset等。不過(guò)其中最常用的當(dāng)然是CSS規(guī)則咯,是CSSStyleRule類型,有下面這些屬性:
- cssText:文本咯,只讀的,包括選擇符,花括號(hào)等等一整套
- parentRule:如果當(dāng)前規(guī)則是導(dǎo)入的規(guī)則,這個(gè)屬性引用導(dǎo)入規(guī)則,否則為null
- parentStyleSheet:規(guī)則所歸屬的樣式表
- selectorText:規(guī)則的選擇符文本
- style:CSSStyleDeclaration 對(duì)象,通過(guò)這個(gè)可以設(shè)置和取得規(guī)則中的樣式值
- type:規(guī)則類型的常量值
var sheet = document.styleSheets[0]; //取得樣式表
var rules = sheet.cssRules || sheet.rules; //為兼容IE
var rule = rules[0];
alert(rule.selectorText);
alert(rule.cssText);
alert(rule.style.cssText);
alert(rule.style.height);
//這里的修改并不成功????
rule.style.height = "2000ps";
alert(rule.style.height);
創(chuàng)建規(guī)則
//添加規(guī)則
function insertRule(sheet, selectorText, cssText, position){
if (sheet.insertRule){
sheet.insertRule(selectorText + "{" + cssText + "}", position);
} else if (sheet.addRule){
sheet.addRule(selectorText, cssText, position);
}
}
刪除規(guī)則
//刪除規(guī)則
function deleteRule(sheet, index){
if (sheet.deleteRule){
sheet.deleteRule(index);
} else if (sheet.removeRule){
sheet.removeRule(index);
}
}
元素尺寸大小
偏移量
元素的可見(jiàn)大小由其內(nèi)容,內(nèi)邊距,滾動(dòng)條和邊框大小決定,并不包括外邊距。有下面4個(gè)屬性:
- offsetHeight
- offsetWidth
- offsetLeft
- offsetTop
其中offsetLeft和offsetTop是相對(duì)包含它的元素而言的。包含元素的引用保存在offsetParent中,這個(gè)并不一定是元素的父節(jié)點(diǎn),而是父節(jié)點(diǎn)中第一個(gè)有大小的元素,比如td的就是table而不是tbody。
由于這里的偏移都是基于父元素的,想要獲得絕對(duì)偏移就需要迭代父元素。
偏移量屬性只讀,每次讀取時(shí)是現(xiàn)計(jì)算的。代價(jià)比較大,最好保存起來(lái)用。
//獲得元素絕對(duì)上偏移
function getElementTop(element){
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current !== null){
actualTop += current. offsetTop;
current = current.offsetParent;
}
return actualTop;
}
客戶區(qū)大小
clientWidth、clientHeight包括內(nèi)邊距和內(nèi)容
滾動(dòng)大小
- scrollHeight:元素內(nèi)容真正的高度
- scrollWidth:真正的寬度
- scrollLeft:被隱藏的內(nèi)容區(qū)域左側(cè)的像素?cái)?shù)
- scrollTop:頂部的
scrollLeft和scrollTop都可以設(shè)置,用來(lái)自動(dòng)滾動(dòng)元素。
確定元素大小
每個(gè)元素有個(gè)方法:getBoundingClientRect()
這個(gè)方法返回一個(gè)矩形對(duì)象,包含 4個(gè)屬性left,top,right,bottom。不過(guò)有個(gè)小問(wèn)題,IE8及以前的版本會(huì)認(rèn)為文檔左上角的坐標(biāo)是(2,2),其他的都是正常的(0,0)。
還有就是有的老瀏覽器可能不支持這個(gè)方法,使用之前的getElementLeft()函數(shù)得到left,再加加offsetWidth得到right。
function getBoundingClientRect(element){
var scrollTop = document.documentElement.scrollTop;
var scrollLeft = document.documentElement.scrollLeft;
//在支持getBoundingClientRect方法的情況下
if (element.getBoundingClientRect){
//這里利用了函數(shù)自身的屬性,如果這個(gè)函數(shù)剛才已經(jīng)執(zhí)行過(guò)了。arguments.callee.offset就已經(jīng)存在了
//就說(shuō)明這個(gè)瀏覽器的調(diào)整量已經(jīng)設(shè)置過(guò)了,直接使用就好了。就不必執(zhí)行下面這個(gè)開(kāi)銷比較大的代碼塊了
if (typeof arguments.callee.offset != "number"){
//利用一個(gè)新元素,將他設(shè)置在瀏覽器的左上角,再獲取它的top值
//看看這個(gè)瀏覽器的偏差是多少,反向減掉
var temp = document.createElement("div");
temp.style.cssText = "position:absolute;left:0;top:0;";
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset
};
//在支持getBoundingClientRect方法的情況下,使用之前的getElementLeft()函數(shù)得到left,再加加offsetWidth得到right
//這個(gè)方法可能不太準(zhǔn)確,不過(guò)誰(shuí)叫你不支持getBoundingClientRect的
} else {
var actualLeft = getElementLeft(element);
var actualTop = getElementTop(element);
return {
left: actualLeft - scrollLeft,
right: actualLeft + element.offsetWidth - scrollLeft,
top: actualTop - scrollTop,
bottom: actualTop + element.offsetHeight - scrollTop
}
}
}
var rect = getBoundingClientRect(document.getElementById("myDiv"));
alert(rect.bottom);
alert(rect.top);
alert(rect.left);
alert(rect.right);
遍歷
DOM2級(jí)遍歷和范圍模塊定義了兩個(gè)用于輔助完成順序遍歷DOM結(jié)構(gòu)的類型。NodeIterator和TreeWalker。這兩個(gè)類型能夠基于給定的節(jié)點(diǎn)進(jìn)行深度優(yōu)先遍歷。IE不支持。
檢測(cè)兼容:
var supportsTraversals = document.implementation.hasFeature("Traversal", "2.0");
var supportsNodeIterator = (typeof document.createNodeIterator == "function");
var supportsTreeWalker = (typeof document.createTreeWalker == "function");
NodeIterator
document.createNodeIterator()來(lái)創(chuàng)建,這個(gè)方法有4個(gè)參數(shù):
- root:搜索起點(diǎn)
- whatToShow:要遍歷那些類型的節(jié)點(diǎn) ?
- filter:NodeFilter對(duì)象,或者表示接受還是拒絕某種節(jié)點(diǎn)的函數(shù)
- entityReferenceExpansion:在HTML中沒(méi)啥用
whatToShow通過(guò)應(yīng)用一個(gè)或多個(gè)過(guò)濾器來(lái)確定要訪問(wèn)哪些節(jié)點(diǎn)。位掩碼。
- NodeFilter.SHOW_ALL
- NodeFilter.SHOW_ELEMENT
- NodeFilter.SHOW_ATTRIBUTE
- NodeFilter.SHOW_TEXT
- NodeFilter.SHOW_CDATA_SECTION
- NodeFilter.SHOW_ENTITY_REFERENCE
- NodeFilter.SHOW_ENTITYE
- NodeFilter.SHOW_PROCESSING_INSTRUCTION
- NodeFilter.SHOW_COMMENT
- NodeFilter.SHOW_DOCUMENT
- NodeFilter.SHOW_DOCUMENT_TYPE
- NodeFilter.SHOW_DOCUMENT_FRAGMENT
- NodeFilter.SHOW_NOTATION
var whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT;
var filter = {
acceptNode: function(node){
return node.tagName.toLowerCase() == "p" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
}
};
//直接定義函數(shù)也行
// var filter = function(node){
// return node.tagName.toLowerCase() == "p" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
// };
var iterator = document.createNodeIterator(document.documentElement, NodeFilter.SHOW_ELEMENT, filter, false);
//或者遍歷所有節(jié)點(diǎn)
iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, null, false);
var node = iterator.nextNode();
while (node !== null) {
alert(node.tagName);
node = iterator.nextNode();
}
NodeIterator的兩個(gè)方法是nextNode()和previousNode(),在遍歷到頭的時(shí)候,這兩個(gè)方法返回null。
TreeWalker
TreeWalker是高級(jí)的NodeIterator:
- parentNode() ?
- firstChild() ?
- lastChild() ?
- nextSibling() ?
- previousSibling()
- nextNode()
- previousNode()
創(chuàng)建和NodeIterator接受一樣的參數(shù),不過(guò)過(guò)濾器在這里有些不同,有3種返回值:NodeFilter.FILTER_SKIP、NodeFilter.FILTER_REJECT、NodeFilter.FILTER_ACCEPT。reject會(huì)跳過(guò)該節(jié)點(diǎn)及其子樹,skip就單單跳過(guò)這個(gè)節(jié)點(diǎn)。
var walker = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_ELEMENT, null, false);
walker.firstChild();
walker.nextSibling();
var node = walker.firstChild();
while (node !== null) {
alert(node.tagName);
node = walker.nextSibling();
}
它還有一個(gè)currentNode屬性,表示上一次遍歷的節(jié)點(diǎn),這個(gè)屬性可以設(shè)置,就改變了繼續(xù)遍歷的起點(diǎn)。
IE啥都不支持
范圍
范圍用來(lái)選擇文檔中的一個(gè)區(qū)域而不必考慮節(jié)點(diǎn)的界限。在常規(guī)的DOM操作不能更有效的修改文檔時(shí),范圍往往可以實(shí)現(xiàn)目的。除IE外都支持,IE8及以前有自己的實(shí)現(xiàn)方式。
DOM中的范圍(除IE8及以下瀏覽器外支持的范圍)
使用createRange()方法創(chuàng)建DOM范圍。新創(chuàng)建的范圍與創(chuàng)建它的文檔相關(guān)聯(lián),不能用于其它文檔。在創(chuàng)建了范圍并設(shè)置了其位置之后,可以針對(duì)范圍的內(nèi)容實(shí)現(xiàn)多種操作,從而實(shí)現(xiàn)對(duì)底層DOM樹的更精細(xì)的控制。
每個(gè)范圍由一個(gè)Range類型的實(shí)例表示,這個(gè)實(shí)例由很多屬性和方法,下列屬性提供了當(dāng)前范圍在文檔中的位置信息。
- startContainer:包含范圍起點(diǎn)的節(jié)點(diǎn),也就是選區(qū)中第一個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)
- startOffset:范圍在startContainer中起點(diǎn)的偏移量。如果startContainer是文本節(jié)點(diǎn)、注釋節(jié)點(diǎn)或CDATA節(jié)點(diǎn),那么startOffset就是范圍起點(diǎn)之前跳過(guò)的字符數(shù)量。否則就是范圍中第一個(gè)子節(jié)點(diǎn)在父節(jié)點(diǎn)(startContainer)中的索引
- endContainer:包含范圍終點(diǎn)的節(jié)點(diǎn)
- endOffset:范圍在endContainer中終點(diǎn)的偏移量
- commonAncestorContainer:startContainer和endContainer共同的祖先節(jié)點(diǎn)在文檔樹中位置最深的那個(gè)
用DOM范圍實(shí)現(xiàn)簡(jiǎn)單選擇
selectNode()、selectNodeContents()
這兩個(gè)方法都接受一個(gè)DOM節(jié)點(diǎn)作為參數(shù),然后使用該節(jié)點(diǎn)中的信息來(lái)填充范圍,selectNode()選擇整個(gè)節(jié)點(diǎn)包括子節(jié)點(diǎn);selectNodeContents()只選擇節(jié)點(diǎn)的子節(jié)點(diǎn)。
<body>
<div id="myDiv" data-appId="12345" data-myname="Nicholas">
哈哈哈我在div里
<span>測(cè)試Span</span>
<a>我是一個(gè)a標(biāo)簽~~~~~</a>
</div>
</body>
var range1 = document.createRange();
var range2 = document.createRange();
var div = document.getElementById("myDiv");
range1.selectNode(div);
range2.selectNodeContents(div);
alert(range1.startContainer.tagName); //body
alert(range1.endContainer.tagName); //body
alert(range1.commonAncestorContainer.tagName); //body
alert(range1.startOffset); //這個(gè)div在body中的索引哦,1
alert(range1.endOffset); //startOffset+1,因?yàn)橹贿x擇了一個(gè)節(jié)點(diǎn)
alert(range2.startContainer.tagName); //div
alert(range2.endContainer.tagName); //div
alert(range2.commonAncestorContainer.tagName); //div
alert(range2.startOffset); //永遠(yuǎn)都是0
alert(range2.endOffset); //子節(jié)點(diǎn)數(shù)目
如果想要更精細(xì)的控制,有下面這些方法:
- setStartBefore(refNode):將范圍起點(diǎn)設(shè)置在refNode之前,refNode就成為了范圍中第一個(gè)節(jié)點(diǎn)。startContainer會(huì)被設(shè)為refNode.parentNode,startOffset會(huì)被設(shè)成refNode在其父節(jié)點(diǎn)childNodes中的索引。
- setStartAfter(refNode)
- setEndBefore(refNode)
- setEndAfter(refNode)
用DOM范圍實(shí)現(xiàn)復(fù)雜選擇
setStart()和setEnd()
這兩個(gè)方法接受兩個(gè)參數(shù):一個(gè)參照節(jié)點(diǎn)和一個(gè)偏移量值。setStart()的參照節(jié)點(diǎn)會(huì)變成startContainer,偏移量會(huì)變成startOffset;setEnd()同理。
var div = document.getElementById("myDiv");
var textNode = div.childNodes[1].firstChild;
var worldNode = div.lastChild;
var range = document.createRange();
range.setStart(textNode, 4);
range.setEnd(worldNode, 0);
alert(range); //an /n 我是一個(gè)a標(biāo)簽~~~~~
操作DOM范圍中的內(nèi)容
創(chuàng)建范圍時(shí),內(nèi)部會(huì)為這個(gè)范圍創(chuàng)建一個(gè)文檔片段,范圍所屬的所有節(jié)點(diǎn)都會(huì)被添加到這個(gè)文檔片段中,為了創(chuàng)建這個(gè)文檔片段,范圍的格式必須正確有效,這就意味著要有正確的DOM結(jié)構(gòu),但是像我們剛才那樣選擇,起始和結(jié)束都在一個(gè)節(jié)點(diǎn)的內(nèi)部,這樣的DOM結(jié)構(gòu)并不正確。不過(guò)范圍知道自己缺少哪些標(biāo)簽,并重新構(gòu)建有效的DOM結(jié)構(gòu)。不過(guò)在你真正對(duì)DOM做出修改之前,范圍是不會(huì)修改DOM結(jié)構(gòu)的,也確實(shí)沒(méi)必要。
在創(chuàng)建了范圍之后,就可以使用各種方法對(duì)范圍的內(nèi)容進(jìn)行操了,表示范圍的內(nèi)部文檔片段中所有節(jié)點(diǎn)都只是指向文檔中相應(yīng)節(jié)點(diǎn)的指針。
- deleteContents():刪除范圍中所包含的內(nèi)容。
- extractContents():同樣是刪除,只不過(guò)會(huì)返回范圍的文檔片段。可以將其插入其他地方。
- cloneContents():復(fù)制內(nèi)容
var fragment = range.extractContents();
document.getElementById("myButton").parentNode.appendChild(fragment);
插入DOM范圍中的內(nèi)容
對(duì)于這里的方法要注意,范圍并不會(huì)在使用這里的方法的時(shí)候自動(dòng)創(chuàng)建有效的DOM結(jié)構(gòu),這對(duì)insertNode()的影響不大,但是對(duì) surroundContents()就有影響了,因?yàn)檫@很可能出現(xiàn)錯(cuò)亂的DOM結(jié)構(gòu)。
insertNode()方法在范圍選區(qū)的開(kāi)始插入一個(gè)節(jié)點(diǎn)
var span = document.createElement("span");
span.style.color = "red";
span.appendChild(document.createTextNode("Inserted text"));
range.insertNode(span);
surroundContents()環(huán)繞范圍插入節(jié)點(diǎn),后臺(tái)會(huì)做這些事情:
- 提取范圍中的內(nèi)容
- 將給定節(jié)點(diǎn)插入到文檔中原來(lái)范圍所在位置
- 將文檔片段內(nèi)容添加到給定節(jié)點(diǎn)中
如果你選中的是像之前那樣不完整的DOM節(jié)點(diǎn)。。。那這里就會(huì)添加失敗。。。這里范圍不會(huì)自己創(chuàng)建有意義的DOM結(jié)構(gòu)
var div = document.getElementById("myDiv");
var textNode = div.childNodes[1].firstChild;
var range = document.createRange();
range.selectNode(textNode);
var span = document.createElement("span");
span.style.backgroundColor = "yellow";
range.surroundContents(span);
alert(range);
折疊DOM范圍
collapse()
折疊后的范圍不選擇文檔的任何部位,傳入true可以折疊到范圍起始位置,false折疊到范圍結(jié)束位置。通過(guò)范圍的collapsed可以檢測(cè)是否折疊,這個(gè)屬性就是檢測(cè)范圍的起始和結(jié)尾是不是同一個(gè)位置,如果是,就算不是使用collapse()折疊的也會(huì)返回true。這個(gè)可以用來(lái)檢測(cè)范圍是不是空的。
比較DOM范圍
compareBoundaryPoints()
比較兩個(gè)范圍是否有公共起點(diǎn)和終點(diǎn)
alert(range1.compareBoundaryPoints(Range.START_TO_START, range2));
Range.START_TO_START(0) ?比較起點(diǎn)
Range.START_TO_END(1) ? 第一個(gè)的起點(diǎn)和第二個(gè)的終點(diǎn)
Range.END_TO_END(2) ?
Range.END_TO_START(3)
第一個(gè)點(diǎn)在第二個(gè)前面-1,相等0,后面1。
復(fù)制DOM范圍
var newRange = range.cloneRange();
清理DOM范圍
range.detach(); //從文檔中分離
range = null; //解除引用
IE8及更早版本中的范圍
IE8以及之前的版本不支持DOM范圍,但是支持一種文本范圍。
可以在文檔和元素上創(chuàng)建文本范圍,在元素上創(chuàng)建的文本范圍只能在本元素內(nèi)使用。
var range = document.body.createTextRange();
簡(jiǎn)單選擇
findText()接收一個(gè)字符串,可選的傳入方向值,返回一個(gè)布爾
這個(gè)方法會(huì)找到第一次出現(xiàn)的給定文本,并將范圍移過(guò)來(lái)環(huán)繞該文本。
var range = document.body.createTextRange();
var found = range.findText("我是");
var foundAgain = range.findText("我是", 1);
alert(found); //true
alert(range.text);
alert(foundAgain);
alert(range.text);
moveToElementText()接收一個(gè)節(jié)點(diǎn),并選擇這個(gè)節(jié)點(diǎn)所有的文本,如果這個(gè)元素里有HTML標(biāo)簽,使用htmlText屬性可以同時(shí)獲取到標(biāo)簽和文本。
range.moveToElementText(div);
alert(range.text);
alert(range.htmlText);
parentElement()可以得到選區(qū)的父節(jié)點(diǎn)
復(fù)雜選擇
move()、moveStart()、moveEnd()、expand()
這些方法都接收兩個(gè)參數(shù),移動(dòng)單位和移動(dòng)單位的數(shù)量,移動(dòng)單位是字符串:"character"、? "word"、? "sentence"、"textedit"。
expand("word")會(huì)將現(xiàn)有的選區(qū)里單詞不全的選全。
move會(huì)先折疊選區(qū),再將范圍移動(dòng)指定的單位數(shù)量。然后再moveStart()、moveEnd()手動(dòng)展開(kāi)選區(qū)。
操作內(nèi)容
range.text = "Howdy";
range.pasteHTML("<em>Howdy</em>");
折疊范圍
collapse()
這個(gè)倒是和DOM范圍一樣。不過(guò)檢測(cè)時(shí)要使用boundingWidth、boundingHeight、boundingLeft、boundingTop這些是范圍的尺寸信息,以像素為單位,boundingWidth為0就代表范圍折疊了。
比較范圍
compareEndPoints()
這個(gè)是差不多的方法,"StartToStart""StartToEnd""EndToEnd""EndToStart"
range1.compareEndPoints("StartToStart", range2)
還有兩個(gè)特別的方法:
range1.isEqual(range2)
range1.inRange(range2)
復(fù)制IE范圍
var newRange = range.duplicate();