JavaScript高級程序設計-DOM2 和 DOM3

12 DOM2和DOM3

DOM1 級主要定義的是 HTML 和 XML 文檔的底層結構。 DOM2 和 DOM3 級則在這個結構的基礎上引入了更多的交互能力,也支持了更高級的 XML 特性。

12.1 DOM變化

DOM2 級和 3 級的目的在於擴展 DOM API,以滿足操作 XML 的所有需求,同時提供更好的錯誤處理及特性檢測能力。

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");

12.1.1 針對XML命名空間的變化

“DOM2 級核心”通過爲大多數 DOM1 級方法提供特定於命名空間的版本解決了這個問題。

  1. Node 類型的變化
    在 DOM2 級中, Node 類型包含下列特定於命名空間的屬性。
  • localName:不帶命名空間前綴的節點名稱。
  • namespaceURI:命名空間 URI 或者(在未指定的情況下是) null。
  • prefix:命名空間前綴或者(在未指定的情況下是) null。

DOM3 級在此基礎上更進一步,又引入了下列與命名空間有關的方法。

  • isDefaultNamespace(namespaceURI):在指定的 namespaceURI 是當前節點的默認命名空
    間的情況下返回 true。
  • lookupNamespaceURI(prefix):返回給定 prefix 的命名空間。
  • lookupPrefix(namespaceURI):返回給定 namespaceURI 的前綴。
  1. Document 類型的變化
    DOM2 級中的 Document 類型也發生了變化,包含了下列與命名空間有關的方法。
  • createElementNS(namespaceURI, tagName):使用給定的 tagName 創建一個屬於命名空間 namespaceURI 的新元素。
  • createAttributeNS(namespaceURI, attributeName):使用給定的 attributeName 創建一個屬於命名空間 namespaceURI 的新特性。
  • getElementsByTagNameNS(namespaceURI, tagName):返回屬於命名空間 namespaceURI的 tagName 元素的 NodeList
//創建一個新的 SVG 元素
var svg = document.createElementNS("http://www.w3.org/2000/svg","svg");
//創建一個屬於某個命名空間的新特性
var att = document.createAttributeNS("http://www.somewhere.com", "random");
//取得所有 XHTML 元素
var elems = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "*");
  1. Element類型的變化

“DOM2 級核心”中有關 Element 的變化,主要涉及操作特性。新增的方法如下。

  • getAttributeNS(namespaceURI,localName):取得屬於命名空間 namespaceURI 且名爲localName 的特性。
  • getAttributeNodeNS(namespaceURI,localName):取得屬於命名空間 namespaceURI 且名爲 localName 的特性節點。
  • getElementsByTagNameNS(namespaceURI, tagName):返回屬於命名空間 namespaceURI的 tagName 元素的 NodeList。
  • hasAttributeNS(namespaceURI,localName):確定當前元素是否有一個名爲 localName的特性,而且該特性的命名空間是 namespaceURI。注意, “DOM2 級核心”也增加了一個hasAttribute()方法,用於不考慮命名空間的情況。
  • removeAttriubteNS(namespaceURI,localName):刪除屬於命名空間 namespaceURI 且名爲 localName 的特性。
  • setAttributeNS(namespaceURI,qualifiedName,value):設置屬於命名空間 namespaceURI 且名爲 qualifiedName 的特性的值爲 value。
  • setAttributeNodeNS(attNode):設置屬於命名空間 namespaceURI 的特性節點。
  1. NamedNodeMap 類型的變化
    NamedNodeMap 類型也新增了下列與命名空間有關的方法。由於特性是通過 NamedNodeMap 表示的,因此這些方法多數情況下只針對特性使用。
  • getNamedItemNS(namespaceURI,localName):取得屬於命名空間 namespaceURI 且名爲localName 的項。
  • removeNamedItemNS(namespaceURI,localName):移除屬於命名空間 namespaceURI 且名爲 localName 的項。
  • setNamedItemNS(node):添加 node,這個節點已經事先指定了命名空間信息。

12.1.2 其他方面的變化

  1. DocumentType 類型的變化
    DocumentType 類型新增了 3 個屬性: publicId、 systemId 和 internalSubset。其中,前兩個屬性表示的是文檔類型聲明中的兩個信息段,這兩個信息段在 DOM1 級中是沒有辦法訪問到的。
  2. Document 類型的變化
    Document 類型的變化中唯一與命名空間無關的方法是 importNode()。這個方法的用途是從一個文檔中取得一個節點,然後將其導入到另一個文檔,使其成爲這個文檔結構的一部分。
  3. Node 類型的變化
    Node 類型中唯一與命名空間無關的變化,就是添加了 isSupported()方法。與 DOM1 級爲 document.implementation 引入的 hasFeature()方法類似, isSupported()方法用於確定當前節點具有什麼能力。這個方法也接受相同的兩個參數:特性名和特性版本號。如果瀏覽器實現了相應特性,而且能夠基於給定節點執行該特性, isSupported()就返回 true。
if (document.body.isSupported("HTML", "2.0")){
//執行只有"DOM2 級 HTML"才支持的操作
}

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
  1. 框架的變化
    框架和內嵌框架分別用 HTMLFrameElement 和 HTMLIFrameElement 表示, 它們在 DOM2 級中都有了一個新屬性,名叫 contentDocument。這個屬性包含一個指針,指向表示框架內容的文檔對象。在此之前,無法直接通過元素取得這個文檔對象(只能使用 frames 集合)
var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument; //在 IE8 以前的版本中無效

由於 contentDocument 屬性是 Document 類型的實例,因此可以像使用其他 HTML 文檔一樣使用它,包括所有屬性和方法。IE8 之前不支持框架中
的 contentDocument 屬性,但支持一個名叫 contentWindow 的屬性,該屬性返回框架的 window 對象,而這個 window 對象又有一個 document 屬性。

var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;

所有瀏覽器都支持 contentWindow 屬性

12.2 樣式

var supportsDOM2CSS = document.implementation.hasFeature("CSS", "2.0");
var supportsDOM2CSS2 = document.implementation.hasFeature("CSS2", "2.0");

12.2.1 訪問元素的樣式

任何支持 style 特性的 HTML 元素在 JavaScript 中都有一個對應的 style 屬性。 這個 style 對象是 CSSStyleDeclaration 的實例,包含着通過 HTML 的 style 特性指定的所有樣式信息,但不包含與外部樣式表或嵌入樣式表經層疊而來的樣式。
CSS 屬性名,必須將其轉換成駝峯大小寫形式,才能通過 JavaScript 來訪問。

CSS屬性 JavaScript屬性
background-image style.backgroundImage
color style.color
display style.display
font-family style.fontFamily

其中一個不能直接轉換的 CSS 屬性就是 float。由於 float 是 JavaScript 中的保留字,因此不能用作屬性名。“DOM2 級樣式”規範規定樣式對象上相應的屬性名應該是 cssFloat; Firefox、 Safari、 Opera 和 Chrome 都支持這個屬性,而 IE支持的則是 styleFloat。

var myDiv = document.getElementById("myDiv");
//設置背景顏色
myDiv.style.backgroundColor = "red";
//改變大小
myDiv.style.width = "100px";
myDiv.style.height = "200px";
//指定邊框
myDiv.style.border = "1px solid black";
  1. DOM 樣式屬性和方法
    “DOM2 級樣式”規範還爲 style 對象定義了一些屬性和方法。這些屬性和方法在提供元素的 style特性值的同時,也可以修改樣式。下面列出了這些屬性和方法。
  • cssText:如前所述,通過它能夠訪問到 style 特性中的 CSS 代碼。
  • length:應用給元素的 CSS 屬性的數量。
  • parentRule:表示 CSS 信息的 CSSRule 對象。本節後面將討論 CSSRule 類型。
  • getPropertyCSSValue(propertyName):返回包含給定屬性值的 CSSValue 對象。
  • getPropertyPriority(propertyName):如果給定的屬性使用了!important 設置,則返回"important";否則,返回空字符串。
  • getPropertyValue(propertyName):返回給定屬性的字符串值。
  • item(index):返回給定位置的 CSS 屬性的名稱。
  • removeProperty(propertyName):從樣式中刪除給定屬性。
  • setProperty(propertyName,value,priority):將給定屬性設置爲相應的值,並加上優先權標誌( "important"或者一個空字符串)。
  1. 計算的樣式
    雖然 style 對象能夠提供支持 style 特性的任何元素的樣式信息,但它不包含那些從其他樣式表層疊而來並影響到當前元素的樣式信息。
<!DOCTYPE html>
<html>
<head>
<title>Computed Styles Example</title>
    <style type="text/css">
        #myDiv {
        background-color: blue;
        width: 100px;
        height: 200px;
        }
    </style>
</head>
<body>
    <div id="myDiv" style="background-color: red; border: 1px solid black"></div>
</body>
</html>
var myDiv = document.getElementById("myDiv");
var computedStyle = document.defaultView.getComputedStyle(myDiv, null);
alert(computedStyle.backgroundColor); // "red"
alert(computedStyle.width); // "100px"
alert(computedStyle.height); // "200px"
alert(computedStyle.border); // 在某些瀏覽器中是"1px solid black"

在這個元素計算後的樣式中,背景顏色的值是"red",寬度值是"100px",高度值是"200px"。我們注意到,背景顏色不是"blue",因爲這個樣式在自身的 style 特性中已經被覆蓋了。IE 不支持 getComputedStyle()方法,但它有一種類似的概念。在 IE 中,每個具有 style 屬性的元素還有一個 currentStyle 屬性。

var myDiv = document.getElementById("myDiv");
var computedStyle = myDiv.currentStyle;
alert(computedStyle.backgroundColor); //"red"
alert(computedStyle.width); //"100px"
alert(computedStyle.height); //"200px"
alert(computedStyle.border); //undefined

12.2.2 操作樣式表

CSSStyleSheet 類型表示的是樣式表,包括通過<link>元素包含的樣式表和在<style>元素中定義的樣式表。使用下面的代碼可以確定瀏覽器是否支持 DOM2 級樣式表。

var supportsDOM2StyleSheets = document.implementation.hasFeature("StyleSheets", "2.0");

從StyleSheet 接口繼承而來的屬性如下。

  • disabled:表示樣式表是否被禁用的布爾值。這個屬性是可讀/寫的,將這個值設置爲 true 可以禁用樣式表。
  • href:如果樣式表是通過<link>包含的,則是樣式表的 URL;否則,是 null。
  • media:當前樣式表支持的所有媒體類型的集合。與所有 DOM 集合一樣,這個集合也有一個length 屬性和一個 item()方法。也可以使用方括號語法取得集合中特定的項。如果集合是空列表,表示樣式表適用於所有媒體。在 IE 中, media 是一個反映<link>和<style>元素 media特性值的字符串。
  • ownerNode:指向擁有當前樣式表的節點的指針,樣式表可能是在 HTML 中通過<link>或
    <style/>引入的(在 XML 中可能是通過處理指令引入的)。如果當前樣式表是其他樣式表通過@import 導入的,則這個屬性值爲 null。 IE 不支持這個屬性。
  • parentStyleSheet:在當前樣式表是通過@import 導入的情況下,這個屬性是一個指向導入它的樣式表的指針。
  • title: ownerNode 中 title 屬性的值。
  • type:表示樣式表類型的字符串。對 CSS 樣式表而言,這個字符串是"type/css"。
    除 了 disabled 屬 性 之 外, 其 他 屬 性都 是 只 讀 的 。 在 支 持 以上 所 有 這 些屬 性 的 基 礎上 ,
    CSSStyleSheet 類型還支持下列屬性和方法:
  • cssRules:樣式表中包含的樣式規則的集合。 IE 不支持這個屬性,但有一個類似的 rules 屬性。
  • ownerRule:如果樣式表是通過@import 導入的,這個屬性就是一個指針,指向表示導入的規則;否則,值爲 null。 IE 不支持這個屬性。
  • deleteRule(index):刪除 cssRules 集合中指定位置的規則。 IE 不支持這個方法,但支持一個類似的 removeRule()方法。
  • insertRule(rule,index):向 cssRules 集合中指定的位置插入 rule 字符串。 IE 不支持這個方法,但支持一個類似的 addRule()方法。
    這裏的 getStyleSheet()返回的樣式表對象與 document.styleSheets 集合中的樣式表對象相同。
  1. CSS 規則
    CSSRule 對象表示樣式表中的每一條規則。實際上, CSSRule 是一個供其他多種類型繼承的基類型,其中最常見的就是 CSSStyleRule 類型,表示樣式信息(其他規則還有@import、 @font-face、
    @page 和@charset,但這些規則很少有必要通過腳本來訪問)。 CSSStyleRule 對象包含下列屬性。
  • cssText:返回整條規則對應的文本。由於瀏覽器對樣式表的內部處理方式不同,返回的文本可能會與樣式表中實際的文本不一樣; Safari 始終都會將文本轉換成全部小寫。 IE 不支持這個屬性。
  • parentRule:如果當前規則是導入的規則,這個屬性引用的就是導入規則;否則,這個值爲null。 IE 不支持這個屬性。
  • parentStyleSheet:當前規則所屬的樣式表。 IE 不支持這個屬性。
  • selectorText:返回當前規則的選擇符文本。由於瀏覽器對樣式表的內部處理方式不同,返回的文本可能會與樣式表中實際的文本不一樣(例如, Safari 3 之前的版本始終會將文本轉換成全部小寫)。在 Firefox、 Safari、 Chrome 和 IE 中這個屬性是隻讀的。 Opera 允許修改 selectorText。
  • style:一個 CSSStyleDeclaration 對象,可以通過它設置和取得規則中特定的樣式值。
  • type:表示規則類型的常量值。對於樣式規則,這個值是 1。 IE 不支持這個屬性。其中三個最常用的屬性是 cssText、 selectorText 和 style。
  1. 創建規則
    DOM 規定,要向現有樣式表中添加新規則,需要使用 insertRule()方法。這個方法接受兩個參數:規則文本和表示在哪裏插入規則的索引。
  2. 刪除規則
    從樣式表中刪除規則的方法是 deleteRule(),這個方法接受一個參數:要刪除的規則的位置。
sheet.deleteRule(0); //DOM 方法
//IE 支持的類似方法叫 removeRule(),使用方法相同,如下所示:
sheet.removeRule(0); //僅對 IE 有效
function deleteRule(sheet,index){
    if(sheet.deleteRule){
        sheet.deleteRule(index);
    }else if(sheet.removeRule){
        sheet.removeRule(index);
    }
}
}

12.2.3 元素大小

  1. 偏移量
    首先要介紹的屬性涉及偏移量(offset dimension),包括元素在屏幕上佔用的所有可見的空間。元素的可見大小由其高度、寬度決定,包括所有內邊距、滾動條和邊框大小(不包括外邊距)。
  • offsetHeight:元素在垂直方向上佔用的空間大小,以像素計。包括元素的高度、(可見的)
    水平滾動條的高度、上邊框高度和下邊框高度。
  • offsetWidth:元素在水平方向上佔用的空間大小,以像素計。包括元素的寬度、(可見的)垂
    直滾動條的寬度、左邊框寬度和右邊框寬度。
  • offsetLeft:元素的左外邊框至包含元素的左內邊框之間的像素距離。
  • offsetTop:元素的上外邊框至包含元素的上內邊框之間的像素距離。
function getElementLeft(element){
    var actualLeft = element.offsetLeft;
    var current = element.offsetParent;
    while(current !==null){
        actualLeft += current.offsetLeft;
        current = current.offsetParent;
    }
    return actualHeight;
}

function getElementTop(element){
    var actualTop = element.offsetTop;
    var current = element.offsetParent;
    while(current!==null){
        actualTop += current.offsetTop;
        current = current.offsetParent;
    }
    return actualTop;
}
  1. 客戶區大小
    元素的客戶區大小(client dimension),指的是元素內容及其內邊距所佔據的空間大小。有關客戶區大小的屬性有兩個: clientWidth 和 clientHeight。其中, clientWidth 屬性是元素內容區寬度加上左右內邊距寬度; clientHeight 屬性是元素內容區高度加上上下內邊距高度。
function getViewport(){
    if(document.compatModez=="BackCompat"){
        return {
            width:document.body.clientWidth,
            height:docuement.body.clientHeight
        };
    }else{
        return {
            width:document.documentElement.clientWidth,
            height:docuement.documentElement.clientHeight
        };
    }
}
  1. 滾動大小
    最後要介紹的是滾動大小(scroll dimension),指的是包含滾動內容的元素的大小。有些元素(例如<html>元素),即使沒有執行任何代碼也能自動地添加滾動條;但另外一些元素,則需要通過 CSS 的overflow 屬性進行設置才能滾動。
  • scrollHeight:在沒有滾動條的情況下,元素內容的總高度。
  • scrollWidth:在沒有滾動條的情況下,元素內容的總寬度。
  • scrollLeft:被隱藏在內容區域左側的像素數。通過設置這個屬性可以改變元素的滾動位置。
  • scrollTop:被隱藏在內容區域上方的像素數。通過設置這個屬性可以改變元素的滾動位置
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pn4U0mcC-1579482366803)(/images/frontend-javascript-dom-scroll.jpg)]
var docHeight = Math.max(document.documentElement.scrollHeight,document.documentElement.clientHeight);
var docWidth = Math.max(document.documentElement.scrollWidth,document.documentElement.clientWidth);
  1. 確定元素大小
    getBoundingClientRect()方法。這個方法返回會一個矩形對象,包含 4 個屬性: left、 top、 right 和 bottom。
function getBoundingClientRect(element){
    if(typeof arguments.callee.offset!="number"){
        var scrollTop = document.documentElementscrollTop;
        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()的瀏覽器,可以通過其他手段取得相同的信息。一般來說, right 和 left 的差值與 offsetWidth 的值相等,而 bottom 和 top 的差值與 offsetHeight相等。而且, left 和 top 屬性大致等於使用本章前面定義的 getElementLeft()和 getElementTop()
函數取得的值。綜合上述,就可以創建出下面這個跨瀏覽器的函數:

function getBoundingClientRect(element){
    var scrollTop = document.documentElement.scrollTop;
    var scrollLeft = document.documentElement.scrollLeft;
    if (element.getBoundingClientRect){
        if(typeof arguments.callee.offset!="number"){
        var scrollTop = document.documentElementscrollTop;
        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
    };
    }else{
        var actualLeft = getElementLeft(element);
        var actualRight = getElementRight(element);

    }return {
        left: actualLeft - scrollLeft,
        right: actualLeft + element.offsetWidth - scrollLeft,
        top: actualTop - scrollTop,
        bottom: actualTop + element.offsetHeight - scrollTop
    }
}

12.3 遍歷

“DOM2 級遍歷和範圍”模塊定義了兩個用於輔助完成順序遍歷 DOM 結構的類型: NodeIterator和 TreeWalker。這兩個類型能夠基於給定的起點對 DOM 結構執行深度優先( depth-first)的遍歷操作。

var supportsTraversals = document.implementation.hasFeature("Traversal", "2.0");
var supportsNodeIterator = (typeof document.createNodeIterator == "function");
var supportsTreeWalker = (typeof document.createTreeWalker == "function");

12.3.1 NodeIterator

NodeIterator 類型是兩者中比較簡單的一個,可以使用 document.createNodeIterator()方法創建它的新實例。

  • root:想要作爲搜索起點的樹中的節點。
  • whatToShow:表示要訪問哪些節點的數字代碼。
  • filter:是一個 NodeFilter 對象,或者一個表示應該接受還是拒絕某種特定節點的函數。
  • entityReferenceExpansion:布爾值,表示是否要擴展實體引用。這個參數在 HTML 頁面中沒有用,因爲其中的實體引用不能擴展。
    whatToShow 參數是一個位掩碼,通過應用一或多個過濾器( filter)來確定要訪問哪些節點。這個參數的值以常量形式在 NodeFilter 類型中定義,如下所示。
  • NodeFilter.SHOW_ALL:顯示所有類型的節點。
  • NodeFilter.SHOW_ELEMENT:顯示元素節點。
  • NodeFilter.SHOW_ATTRIBUTE:顯示特性節點。由於 DOM 結構原因,實際上不能使用這個值。
  • NodeFilter.SHOW_TEXT:顯示文本節點。
  • NodeFilter.SHOW_CDATA_SECTION:顯示 CDATA 節點。對 HTML 頁面沒有用。
  • NodeFilter.SHOW_ENTITY_REFERENCE:顯示實體引用節點。對 HTML 頁面沒有用。
  • NodeFilter.SHOW_ENTITYE:顯示實體節點。對 HTML 頁面沒有用。
  • NodeFilter.SHOW_PROCESSING_INSTRUCTION:顯示處理指令節點。對 HTML 頁面沒有用。
  • NodeFilter.SHOW_COMMENT:顯示註釋節點。
  • NodeFilter.SHOW_DOCUMENT:顯示文檔節點。
  • NodeFilter.SHOW_DOCUMENT_TYPE:顯示文檔類型節點。
  • NodeFilter.SHOW_DOCUMENT_FRAGMENT:顯示文檔片段節點。對 HTML 頁面沒有用。
  • NodeFilter.SHOW_NOTATION:顯示符號節點。對 HTML 頁面沒有用。
    可以通過 createNodeIterator()方法的 filter 參數來指定自定義的 NodeFilter 對象,或者指定一個功能類似節點過濾器( node filter)的函數
var filter = function(node){
    return node.tagName.toLowerCase() == "p" ?
    NodeFilter.FILTER_ACCEPT :
    NodeFilter.FILTER_SKIP;
    };
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT,filter, false);
//下面的代碼創建了一個能夠訪問所有類型節點的簡單的 NodeIterator。
var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL,null, false);

12.3.2 TreeWalker

TreeWalker 是 NodeIterator 的一個更高級的版本。除了包括 nextNode()和 previousNode()在內的相同的功能之外,這個類型還提供了下列用於在不同方向上遍歷 DOM 結構的方法。

  • parentNode():遍歷到當前節點的父節點;
  • firstChild():遍歷到當前節點的第一個子節點;
  • lastChild():遍歷到當前節點的最後一個子節點;
  • nextSibling():遍歷到當前節點的下一個同輩節點;
  • previousSibling():遍歷到當前節點的上一個同輩節點。
var div = document.getElementById("div1");
var filter = function(node){
    return node.tagName.toLowerCase() == "li"?
        NodeFilter.FILTER_ACCEPT :
        NodeFilter.FILTER_SKIP;
};
var walker= document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT,filter, false);
var node = iterator.nextNode();
while (node !== null) {
    alert(node.tagName); //輸出標籤名
    node = iterator.nextNode();
}

12.4 範圍

爲了讓開發人員更方便地控制頁面,“DOM2 級遍歷和範圍”模塊定義了“範圍”( range)接口。通過範圍可以選擇文檔中的一個區域,而不必考慮節點的界限.

12.4.1 DOM中的範圍

DOM2 級在 Document 類型中定義了 createRange()方法。在兼容 DOM 的瀏覽器中,這個方法屬於 document 對象。使用 hasFeature()或者直接檢測該方法,都可以確定瀏覽器是否支持範圍。

var supportsRange = document.implementation.hasFeature("Range", "2.0");
var alsoSupportsRange = (typeof document.createRange == "function");
//如果瀏覽器支持範圍,那麼就可以使用 createRange()來創建 DOM 範圍,如下所示:
var range = document.createRange();
  • startContainer:包含範圍起點的節點(即選區中第一個節點的父節點)。
  • startOffset:範圍在 startContainer 中起點的偏移量。如果 startContainer 是文本節點、註釋節點或 CDATA 節點,那麼 startOffset 就是範圍起點之前跳過的字符數量。否則,
    startOffset 就是範圍中第一個子節點的索引。
  • endContainer:包含範圍終點的節點(即選區中最後一個節點的父節點)。
  • endOffset:範圍在 endContainer 中終點的偏移量(與 startOffset 遵循相同的取值規則)。
  • commonAncestorContainer: startContainer 和 endContainer 共同的祖先節點在文檔樹中位置最深的那個
  1. 用 DOM 範圍實現簡單選擇
    要使用範圍來選擇文檔中的一部分,最簡的方式就是使用 selectNode()或 selectNodeContents()。爲了更精細地控制將哪些節點包含在範圍中,還可以使用下列方法。
  • setStartBefore(refNode):將範圍的起點設置在 refNode 之前,因此 refNode 也就是範圍選區中的第一個子節點。同時會將 startContainer 屬性設置爲 refNode.parentNode,將startOffset 屬性設置爲 refNode 在其父節點的 childNodes 集合中的索引。
  • setStartAfter(refNode):將範圍的起點設置在 refNode 之後,因此 refNode 也就不在範圍之內了,其下一個同輩節點纔是範圍選區中的第一個子節點。同時會將 startContainer 屬性設置爲 refNode.parentNode,將 startOffset 屬性設置爲 refNode 在其父節點的childNodes 集合中的索引加 1。
  • setEndBefore(refNode):將範圍的終點設置在 refNode 之前,因此 refNode 也就不在範圍之內了,其上一個同輩節點纔是範圍選區中的最後一個子節點。同時會將 endContainer 屬性設置爲refNode.parentNode,將 endOffset 屬性設置爲 refNode 在其父節點的 childNodes集合中的索引。
  • setEndAfter(refNode):將範圍的終點設置在 refNode 之後,因此 refNode 也就是範圍選區
    中的最後一個子節點。同時會將 endContainer 屬性設置爲 refNode.parentNode,將endOffset 屬性設置爲 refNode 在其父節點的 childNodes 集合中的索引加 1。
  1. 用 DOM 範圍實現複雜選擇
    要創建複雜的範圍就得使用 setStart()和 setEnd()方法。這兩個方法都接受兩個參數:一個參照節點和一個偏移量值。對 setStart()來說,參照節點會變成 startContainer,而偏移量值會變成startOffset。
可以使用這兩個方法來模仿 selectNode()selectNodeContents()。來看下面的例子:
var range1 = document.createRange();
    range2 = document.createRange();
    p1 = document.getElementById("p1");
    p1Index = -1;
    i, len;
for (i=0, len=p1.parentNode.childNodes.length; i < len; i++) {
    if (p1.parentNode.childNodes[i] == p1) {
    p1Index = i;
    break;
    }
}
range1.setStart(p1.parentNode, p1Index);
range1.setEnd(p1.parentNode, p1Index + 1);
range2.setStart(p1, 0);
range2.setEnd(p1, p1.childNodes.length);
DOMRangeExample2.

顯然,要選擇這個節點(使用 range1),就必須確定當前節點( p1)在其父節點的 childNodes集合中的索引。而要選擇這個節點的內容(使用 range2),也不必計算什麼;只要通過 setStart()和 setEnd()設置默認值即可。
3. 操作 DOM 範圍中的內容
在創建範圍時 ,內部會爲這個範圍創建一個文檔片段,範圍所屬的全部節點都被添加到了這個文檔片段中。爲了創建這個文檔片段,範圍內容的格式必須正確有效。在前面的例子中,我們創建的選區分別開始和結束於兩個文本節點的內部,因此不能算是格式良好的 DOM 結構,也就無法通過 DOM 來表示。
4. 插入 DOM 範圍中的內容
利用範圍,可以刪除或複製內容,還可以像前面介紹的那樣操作範圍中的內容。 使用 insertNode()
方法可以向範圍選區的開始處插入一個節點。假設我們想在前面例子中的 HTML 前面插入以下 HTML
代碼:

<span style="color: red">Inserted text</span>
//那麼,就可以使用下列代碼:
var p1 = document.getElementById("p1");
    helloNode = p1.firstChild.firstChild;
    worldNode = p1.lastChild;
    range = document.createRange();
    range.setStart(helloNode, 2);
    range.setEnd(worldNode, 3);
var span = document.createElement("span");
    span.style.color = "red";
    span.appendChild(document.createTextNode("Inserted text"));
    range.insertNode(span);
  1. 摺疊 DOM 範圍
    所謂摺疊範圍,就是指範圍中未選擇文檔的任何部分。可以用文本框來描述摺疊範圍的過程。假設文本框中有一行文本,你用鼠標選擇了其中一個完整的單詞。然後,你單擊鼠標左鍵,選區消失,而光標則落在了其中兩個字母之間。同樣,在摺疊範圍時,其位置會落在文檔中的兩個部分之間,可能是範圍選區的開始位置,也可能是結束位置。
  2. 比較 DOM 範圍
    在有多個範圍的情況下,可以使用 compareBoundaryPoints()方法來確定這些範圍是否有公共的邊界(起點或終點)。這個方法接受兩個參數:表示比較方式的常量值和要比較的範圍。表示比較方式的常量值如下所示。
  • Range.START_TO_START(0):比較第一個範圍和第二個範圍的起點;
  • Range.START_TO_END(1):比較第一個範圍的起點和第二個範圍的終點;
  • Range.END_TO_END(2):比較第一個範圍和第二個範圍的終點;
  • Range.END_TO_START(3):比較第一個範圍的終點和第一個範圍的起點。
var range1 = document.createRange();
var range2 = document.createRange();
var p1 = document.getElementById("p1");
range1.selectNodeContents(p1);
range2.selectNodeContents(p1);
range2.setEndBefore(p1.lastChild);
alert(range1.compareBoundaryPoints(Range.START_TO_START, range2)); //0
alert(range1.compareBoundaryPoints(Range.END_TO_END, range2)); //1
  1. 複製 DOM 範圍
    可以使用 cloneRange()方法複製範圍。這個方法會創建調用它的範圍的一個副本。
var newRange = range.cloneRange();

新創建的範圍與原來的範圍包含相同的屬性,而修改它的端點不會影響原來的範圍。
8. 清理 DOM 範圍
在使用完範圍之後,最好是調用 detach()方法,以便從創建範圍的文檔中分離出該範圍。調用detach()之後,就可以放心地解除對範圍的引用,從而讓垃圾回收機制回收其內存了。來看下面的例子。

range.detach(); //從文檔中分離
range = null; //解除引用

12.5 小結

DOM2 級規範定義了一些模塊,用於增強 DOM1 級。“DOM2 級核心”爲不同的 DOM 類型引入了一些與 XML 命名空間有關的方法。這些變化只在使用 XML 或 XHTML 文檔時纔有用;對於 HTML 文檔沒有實際意義。除了與 XML 命名空間有關的方法外, “DOM2 級核心”還定義了以編程方式創建Document 實例的方法,也支持了創建 DocumentType 對象。
“DOM2 級樣式”模塊主要針對操作元素的樣式信息而開發,其特性簡要總結如下。

  • 每個元素都有一個關聯的 style 對象,可以用來確定和修改行內的樣式。
  • 要確定某個元素的計算樣式(包括應用給它的所有 CSS 規則), 可以使用 getComputedStyle()方法。
  • IE 不支持 getComputedStyle()方法,但爲所有元素都提供了能夠返回相同信息 currentStyle屬性。
  • 可以通過 document.styleSheets 集合訪問樣式表。
  • 除 IE 之外的所有瀏覽器都支持針對樣式表的這個接口, IE 也爲幾乎所有相應的 DOM 功能提供了自己的一套屬性和方法。
    “DOM2 級遍歷和範圍”模塊提供了與 DOM 結構交互的不同方式,簡要總結如下。
  • 遍歷即使用 NodeIterator 或 TreeWalker 對 DOM 執行深度優先的遍歷。
  • NodeIterator 是一個簡單的接口,只允許以一個節點的步幅前後移動。而 TreeWalker 在提供相同功能的同時,還支持在 DOM 結構的各個方向上移動,包括父節點、同輩節點和子節點等方向。
  • 範圍是選擇 DOM 結構中特定部分,然後再執行相應操作的一種手段。
  • 使用範圍選區可以在刪除文檔中某些部分的同時,保持文檔結構的格式良好,或者複製文檔中的相應部分。
  • IE8 及更早版本不支持“DOM2 級遍歷和範圍”模塊,但它提供了一個專有的文本範圍對象,可以用來完成簡單的基於文本的範圍操作。 IE9 完全支持 DOM 遍歷。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章