48

深入Node.compareDocumentPosition API

 5 years ago
source link: https://www.zhangxinxu.com/wordpress/2019/03/node-comparedocumentposition-api/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

byzhangxinxu from https://www.zhangxinxu.com/wordpress/?p=8527

本文可全文转载,个人网站无需授权,只要保留原作者、出处以及文中链接即可,任何网站均可摘要聚合,商用请联系授权。

yueMRru.png!web

一、快速了解

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)

返回值分别是 1020 。其中:

  • 108+2 的组合值, 8 表示 documentElement 包含 body2 表示 documentElementbody 前面。
  • 2016+4 的组合值, 16 表示 bodydocumentElement 包含, 4 表示 bodydocumentElement 后面。

因此,我们实际开发的时候,不能直接等于 == 某个常量值判断位置关系,而需要借助其他运算符,例如位运算符 & 。在JS中,一个 & 表示运算符按位与,就是把两个二进制数按每一位比较,同时为 1 才得 1 ,只要一个为 0 就为 0 ,最终的二进制值就是运算值。

例如数字 28 比较,如下图:

FVb6rev.png!web

2 & 8;    // 结果是00000,也就是0

如果是数字 2 和数字 10 比较呢?如下图:

Mzmu6vQ.png!web

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),如下图:

jQnYZnY.png!web

四、应用场景

什么时候我们需要知道节点的前后位置关系呢?在一些animation+绝对定位实现的slide页面过场的场景下,由于回到之前页面的slide方向是相反的,此时我们就可以通过节点的前后位置关系判断页面是从右往左出去,还是从左往右出去。

例如这个demo页面,点击底部的选项卡,可以看到不同的过场方向,就是借助compareDocumentPosition方法判断的。

也可以点击下面的视频体验:

五、结语

我还是太年轻,一开始把这个API想的太简单了,以为就是一些位置返回一些固定的数字,没想到返回的位置居然是混合的,好处是我们可以准确知晓某些元素节点在页面中的复杂位置关系,不足就是增加了我们的学习和理解成本。

其实我们实际开发很少有场景需要知道非常详情的位置关系的。

实际开发推荐使用 Node.DOCUMENT_POSITION_DISCONNECTED 这样的常量进行比对,更容易理解,可读性更好。关键问题不好记忆,没办法,到时候查文档,或者到本站搜索 compareDocumentPosition

好了,就说这些,感谢阅读!

Mfm2U3B.png!web

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。

本文地址: https://www.zhangxinxu.com/wordpress/?p=8527

(本篇完)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK