深入Node.compareDocumentPosition API
byzhangxinxu from https://www.zhangxinxu.com/wordpress/?p=8527
本文可全文轉載,個人網站無需授權,只要保留原作者、出處以及文中連結即可,任何網站均可摘要聚合,商用請聯絡授權。
一、快速瞭解
Node.compareDocumentPosition()方法可以用來對比兩個HTML節點在文件中的位置關係,包括前後,父子,自身以及跨文件。不僅是DOM節點,文字節點,註釋節點甚至屬性節點的位置關係都可以判定,很強。
IE9+瀏覽器支援,IE8可以藉助 sourceIndex
來判定。
二、深入理解
compareDocumentPosition
語法如下:
compareValue = node.compareDocumentPosition(otherNode)
注意:這裡有個容易記不清的地方,到底返回的位置關係是 node
相對於 otherNode
,還是 otherNode
相對於 node
呢?結果居然返回的是 otherNode
相對於 node
的位置!
可能是中國文化和外國文化的區別。中國文化內斂,固守眷戀,關注自身,言必吾當如何如何;外國文化向外,武力侵略,指手劃腳,都是你該如何如何。
由於這些API語法是老外發明的,所以,概念上就按照老外的認識來的。節點node對otherNode發起了一個文件位置判斷的挑戰,最終的結果不是我贏了或我輸了,而是你輸了或者你贏了。也就是 otherNode
你在前面, otherNode
你在後面,這樣子的。
返回值
compareValue
是返回值,是整數值,可能的值如下表:
二進位制 | 返回值 | 釋義 | 對應常量 |
---|---|---|---|
000000 | 0 | 節點一致 | – |
000001 | 1 | 節點在不同的文件(或者一個在文件之外) | Node.DOCUMENT_POSITION_DISCONNECTED |
000010 | 2 | 節點 otherNode 在節點 node 之前 | Node.DOCUMENT_POSITION_PRECEDING |
000100 | 4 | 節點 otherNode 在節點 node 之後 | Node.DOCUMENT_POSITION_FOLLOWING |
001000 | 8 | 節點 otherNode 包含節點 node | Node.DOCUMENT_POSITION_CONTAINS |
010000 | 16 | 節點 otherNode 被節點 node 包含 | Node.DOCUMENT_POSITION_CONTAINED_BY |
100000 | 32 | 特定的節點位置,依賴於DOM實現 | Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
– | 組合值(如34,32+2) | 複合節點關係 | – |
關於複合節點關係的組合返回值稍後展開,我們先開看簡單的位置關係熟悉下此API。
// 返回值是 0 document.body.compareDocumentPosition(document.body); // 返回值是 4,<body>在<head>後面 document.head.compareDocumentPosition(document.body)
接下來,我們看下複合節點關係下返回的組合值,如下:
// 返回值是 10,8 + 2 document.body.compareDocumentPosition(document.documentElement); // 返回值是 20,16 + 4 document.documentElement.compareDocumentPosition(document.body)
返回值分別是 10
和 20
。其中:
-
10
是8+2
的組合值,8
表示documentElement
包含body
,2
表示documentElement
在body
前面。 -
20
是16+4
的組合值,16
表示body
被documentElement
包含,4
表示body
在documentElement
後面。
因此,我們實際開發的時候,不能直接等於 ==
某個常量值判斷位置關係,而需要藉助其他運算子,例如位運算子 &
。在JS中,一個 &
表示運算子按位與,就是把兩個二進位制數按每一位比較,同時為 1
才得 1
,只要一個為 0
就為 0
,最終的二進位制值就是運算值。
例如數字 2
和 8
比較,如下圖:
2 & 8;// 結果是00000,也就是0
如果是數字 2
和數字 10
比較呢?如下圖:
2 & 10;// 結果是00010,也就是2
由於 compareDocumentPosition
返回值都是標準的只有1位是1的二進位制值,因此,要判斷前後或者內外節點位置關係直接按位與,結果不是0就可以了。
例如:
if (document.body.compareDocumentPosition(document.documentElement) & Node.DOCUMENT_POSITION_PRECEDING) { // document.documentElement在document.body前面 // ... }
一個字元 &
而不是 ==
喲。
三、進一步深入
compareDocumentPosition
還可以用來比對HTML屬性節點的前後位置關係,例如如下HTML:
<img id="compareImg" src="./mm.jpg" alt="示意圖">
var altNode = compareImg.getAttributeNode('alt'); var srcNode = compareImg.getAttributeNode('src'); // 結果是34 = 32 + 2 console.log(altNode.compareDocumentPosition(srcNode));
如果HTML程式碼中的 'src'
和 'alt'
屬性位置調換下,如下:
<img id="compareImg" alt="示意圖" src="./mm.jpg">
則結果是:
// 結果是36 = 32 + 4 console.log(altNode.compareDocumentPosition(srcNode));
如何出現返回值1?
當我們的節點在記憶體中而不再文件頁面中,或者我們的節點在頁面內的其它iframe中的時候,會出現返回值包含 1
。
例如:
// 結果是35 = 32 + 2 + 1 document.createElement('div').compareDocumentPosition(document.body)
也就是特定實現(32), document.body
在前(2),兩者文件無關聯(1)。
又例如我們直接 藉助Blob動態建立一個非外鏈iframe ,程式碼如下:
var htmlIframe = '<img id="img" src="https://.../mm.jpg" onclick="console.log(this.compareDocumentPosition(window.parent.document.body))">'; var iframe = document.createElement('iframe'); var blob = new Blob([htmlIframe], { 'type': 'text/html'}); iframe.src = URL.createObjectURL(blob); iframeBlob1.appendChild(iframe);
實時效果如下,點選妹子圖片,看看輸出的結果是?
結果是35(32 + 2 + 1),如下圖:
四、應用場景
什麼時候我們需要知道節點的前後位置關係呢?在一些animation+絕對定位實現的slide頁面過場的場景下,由於回到之前頁面的slide方向是相反的,此時我們就可以通過節點的前後位置關係判斷頁面是從右往左出去,還是從左往右出去。
例如這個demo頁面,點選底部的選項卡,可以看到不同的過場方向,就是藉助compareDocumentPosition方法判斷的。
也可以點選下面的視訊體驗:
五、結語
我還是太年輕,一開始把這個API想的太簡單了,以為就是一些位置返回一些固定的數字,沒想到返回的位置居然是混合的,好處是我們可以準確知曉某些元素節點在頁面中的複雜位置關係,不足就是增加了我們的學習和理解成本。
其實我們實際開發很少有場景需要知道非常詳情的位置關係的。
實際開發推薦使用 Node.DOCUMENT_POSITION_DISCONNECTED
這樣的常量進行比對,更容易理解,可讀性更好。關鍵問題不好記憶,沒辦法,到時候查文件,或者到本站搜尋 compareDocumentPosition
。
好了,就說這些,感謝閱讀!
本文為原創文章,會經常更新知識點以及修正一些錯誤,因此轉載請保留原出處,方便溯源,避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。
本文地址: https://www.zhangxinxu.com/wordpress/?p=8527
(本篇完)