JS學(xué)習(xí)11(DOM2&DOM3)

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ì)做這些事情:

  1. 提取范圍中的內(nèi)容
  2. 將給定節(jié)點(diǎn)插入到文檔中原來(lái)范圍所在位置
  3. 將文檔片段內(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(); 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容