天天看點

contains和compareDocumentPosition 的用法及差別

  1、DOMElement.contains(DOMNode)

    這個方法起先用在 IE ,用來确定 DOM Node 是否包含在另一個 DOM Element 中。

    注意點:如果 DOM Node 和 DOM Element 相一緻,.contains() 将傳回 true ,雖然,一個元素不能包含自己。

    這裡有一個簡單的執行包裝,可以運作在:Internet Explorer, Firefox, Opera, and Safari。

  function contains(a, b) {

       return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(arg) & 16);

  }

  2、NodeA.compareDocumentPosition(NodeB)

    這個方法是 DOM Level 3 specification 的一部分,允許你确定 2 個 DOM Node 之間的互相位置。這個方法比 .contains() 強大。這個方法的一個可能應用是排序 DOM Node 成一個詳細精确的順序,該方法IE6~8是不支援的

    使用這個方法你可以确定關于一個元素位置的一連串的資訊。所有的這些資訊将傳回一個比特碼(Bit,比特,亦稱二進制位)。    對于那些,人們知之甚少。比特碼是将多重資料存儲為一個簡單的數字(譯者注:0 或 1)。你最終打開 / 關閉個别數目(譯者注:打開/關閉對應 0 /1),将給你一個最終的結果。

    這裡是從 NodeA.compareDocumentPosition(NodeB) 傳回的結果,包含你可以得到的資訊。

Bits          Number        Meaning 

000000         0              元素一緻 

000001         1              節點在不同的文檔(或者一個在文檔之外) 

000010         2              節點 B 在節點 A 之前 

000100         4              節點 A 在節點 B 之前 

001000         8              節點 B 包含節點 A 

010000         16             節點 A 包含節點 B 

100000         32             浏覽器的私有使用

現在,這意味着一個可能的結果類似于:

 <div id="a">

 <div id="b"></div>

</div>

<script>

 alert( document.getElementById("a").compareDocumentPosition(document.getElementById("b")) == 20);

</script>

   一旦一個節點 A 包含另一個節點 B,包含 B(+16) 且在 B 之前(+4),則最後的結果是數字 20 。如果你檢視比特發生的變化,将增加你的了解。

000100 (4) + 010000 (16) = 010100 (20)

    這個,毫無疑問,有助于了解單個最混亂的 DOM API 方法。當然,他的價值當之無愧的。

    現在,DOMNode.compareDocumentPosition 在 Firefox 和 Opera 中是可用的。然而,有一些技巧,我們可以用來在 IE 中執行他。

function comparePosition(a, b){

 return a.compareDocumentPosition ?

 a.compareDocumentPosition(b) :

 a.contains ?

  ( a != b && a.contains(b) && 16 ) +

  ( a != b && b.contains(a) && 8 ) +

  ( a.sourceIndex >= 0 && b.sourceIndex >= 0 ?

   (a.sourceIndex < b.sourceIndex && 4 ) +

   (a.sourceIndex > b.sourceIndex && 2 ) :

   1 ) :

  0;

}

    IE 提供給我們一些可以使用的方法和屬性。開始,使用 .contains() 方法(如我們前面所讨論的),以便給我們包含(+16)或者被包含(+8)的結果。IE 還有一個 .sourceIndex 屬性在所有的 DOM Element 對應着元素在文檔中的位置,例如:document.documentElement.sourceIndex == 0。因為我們有這個資訊,我們可以完成兩個 compareDocumentPosition 難題:在前面(+2)和在後面(+4)。另外,如果一個元素不在目前的文檔,.sourceIndex 将等于 -1,這個給我們另外一個回答(+1)。最後,通過這個過程的推斷,我們可以确定如果一個元素等于他本身,傳回一個空的比特碼(+0)。

    這個函數可以在 Internet Explorer、Firefox 和 Opera 中運作。但在 Safari 中卻有殘缺功能(因為他隻有 contains() 方法,而沒有 .sourceIndex 屬性。我們隻能得到 包含(+16),被包含(+8),其他的所有結果都将傳回(+1)代表一個斷開)。

    PPK 提供了一個關于通過建立一個 getElementsByTagNames 方法使新功能可以被使用的很棒的例子。讓我們改編他到我們的新方法中:

// Original by PPK quirksmode.org

function getElementsByTagNames(list, elem) {

        elem = elem || document;         

        var tagNames = list.split(’,’), results = [];         

        for ( var i = 0; i < tagNames.length; i++ ) {

                var tags = elem.getElementsByTagName( tagNames[i] );

                for ( var j = 0; j < tags.length; j++ )

                        results.push( tags[j] );

        }         

        return results.sort(function(a, b){

                return 3 - (comparePosition(a, b) & 6);

        });

}

    我們現在可以使用他來按次序建構一個站點的目錄:

getElementsByTagNames("h1, h2, h3");

    雖然 Firefox 和 Opera 都采取了一些主動落實這一方法。我依然期待看到更多的浏覽器進入,以幫助向前推動

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

__contains =  

    documentElement.compareDocumentPosition ?  

        function (a, b) {  

            return !!(a.compareDocumentPosition(b) & 16);  

        } :  

        documentElement.contains ?  

            function (a, b) {  

                if (a.nodeType == NodeType.DOCUMENT_NODE) {  

                    a = a.documentElement;  

                }  

                // !a.contains => a===document || text  

                // 注意原生 contains 判斷時 a===b 也傳回 true  

                b = b.parentNode;  

                if (a == b) {  

                    return true;  

                }  

                // when b is document, a.contains(b) 不支援的接口 in ie  

                if (b && b.nodeType == NodeType.ELEMENT_NODE) {  

                    return a.contains && a.contains(b);  

                } else {  

                    return false;  

                }  

            } : 0;  

這段代碼的作用是判斷一個節點是另一個節點的父節點。代碼中主要用到了compareDocumentPosition與contains兩個方法。 

下面認識一下這兩個方法。 

contains是IE發明的函數,後來也有别的浏覽器支援,比如Chrome,opera,有資料說firefox不支援此方法,但我用firefox15測試發現已經支援此方法。用法如下: 

el.contains(el2); 

如果 el包含el2 或 el === el2,傳回true;否則傳回false。 

這很簡單,接着說compareDocumentPosition。 

這個函數來自DOM Level 3 規範,标準浏覽器都支援,但IE6-8不支援,IE從9開始支援。這個函數的傳回值有點難了解,下面細說: 

<div id="a">  

    <div id="b"></div>  

</div>  

<script type="text/javascript">  

   alert(a.compareDocumentPosition(b));  

</script>  

在Chrome試試這個例子,結果是 20。這裡的 20 表示什麼呢?下面先看一些常見的傳回值: 

  33 => 100001 

  20 => 010100 

  16 => 010000 

  10 => 001010 

    8 => 001000 

    4 => 000100 

    2 => 000010 

    1 => 000001 

    0 => 000000 

很明顯,例子的結果是第2個。要說明20的意義,需要先分析右邊的二進制數。 

這個二進制數分6位,從左到右依次是(如果符合條件,該位為1,否則為0): 

  第1位:浏覽器的私有使用 

  第2位:a是b的祖先節點 

  第3位:a是b的子孫節點 

  第4位:a是b前面的節點 

  第5位:a是b後面的節點 

  第6位:a和b在不同的文檔(或者一個在文檔之外) 

說到這裡,20的意思就明顯了,即 a是b的祖先節點,同時 a是b前面的節點。其實通常用到的是第2-5位,我覺得這四位已經足夠表示任何位置關系了,也許你會問,a等于b 的情況怎麼表示呢? 

逆向思維一下,如果這四位都是0,也就是說,既不是父節點,又不是子節點,既不在前面,又不在後面,那唯一的解釋就是a 等于 b,答案就是 000000。 

知道了這些再來看代碼 return !!(a.compareDocumentPosition(b) & 16); 

為什麼要&16? 我們知道16的2進制為10000 ,我們發現之後與左數第五位為1的數做&運算才會得到非零數,對非零做兩次!操作就獲得了一個布爾值。那麼謎底解開了,這句的作用就是判斷一個節點是否是另一個節點的祖先節點。 

繼續分析代碼: 

Javascript代碼  收藏代碼

if (a.nodeType == NodeType.DOCUMENT_NODE) {  

    a = a.documentElement;  

}  

// !a.contains => a===document || text  

這段代碼考慮到了一種情況,就是當第一個參數是document的情況,document本身不是一個節點,是以contains方法無法對其進行比較,但是在邏輯上我們可以把document當成是最外層節點來處理,似乎更加合理。 

Javascript代碼  收藏代碼

// 注意原生 contains 判斷時 a===b 也傳回 true  

 b = b.parentNode;  

 if (a == b) {  

     return true;  

 }  

contrains方法判斷a與b對象的關系式還會将a 等于 b這種情況也傳回true。上面這段代碼将這種情況做了特殊處理。 

最後一段代碼就是使用contains方法來判斷a與b的關系。 

*注:在比較節點時有一種特殊情況,就是兩個節點其中一個或兩個還沒有被放入DOM樹中,這種情況下的比較結果結果在個浏覽器中表現不一緻,請使用者注意。 

繼續閱讀