26

DOM小测28期 – DOM节点文档前后位置判断

 5 years ago
source link: https://www.zhangxinxu.com/wordpress/2019/03/dom-contains-comparedocumentposition/?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=8522

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

Bni63mQ.jpg!web

一、题目与考察点

题目地址: https://github.com/zhangxinxu/quiz/issues/9

题目内容如下(点击查看大图):

JfYni2M.jpg!web

本题主要考察如何判断DOM节点文档前后位置,父子关系等。我看了下最后的回答,近9成的回答使用了非常啰嗦的方法,比例之高,实在出乎意料。实际上,本题有非常简单、寥寥数行就能实现的方法,只要你知道下面这两个很有用的DOM原生API,一个是 contains() 方法,判断DOM元素或节点是否有包含关系;另外一个是 compareDocumentPosition() 方法,更强悍的DOM或节点位置关系判断,无论是前后、内外还是跨文档都可以。

本次B站答疑直播在上午10:34分开始,持续约30分钟,有录播,可以直接点击下面的视频观看。

二、DOM包含关系判断contains()

contains() 方法是一个很古老的API,用来判断两个DOM节点之间的包含关系,语法如下:

node.contains(otherNode)

返回布尔值,表示 node 是否包含 otherNode ,或者就是 node 本身。

例如:

document.documentElement.contains(document.body);    // 返回值是true
document.body.contains(document.body);               // 返回值是true
document.body.contains(document.documentElement);    // 返回值是false

此API兼容性良好,IE5就开始支持了,使用非常方便,我们无需专门遍历祖先元素用判断两个节点之间的嵌套关系。

其它

如果判断的两个节点元素是跨iframe文档的,则会被认为是 false 。例如我们直接 借助Blob动态创建一个非外链iframe ,代码如下:

var htmlIframe = '<img id="img" src="https://.../mm.jpg" onclick="console.log(window.parent.document.body.contains(this))">';
var iframe = document.createElement('iframe');
var blob = new Blob([htmlIframe], { 'type': 'text/html'});
iframe.src = URL.createObjectURL(blob);
iframeBlob1.appendChild(iframe);

实时效果如下,点击妹子图片,看看输出的结果是?

控制台输出结果如下:

iiA3Mv6.png!web

如果想要知道iframe内外的包含关系,则需要使用另外的API: compareDocumentPosition()

三、任意位置判断compareDocumentPosition

本题中图片DOM元素前后位置的比对完全不需要写循环进行遍历,有现成的API可以实现我们想要的效果,那就是 Node.compareDocumentPosition API。

此API颇有深度,我专门写了篇文章介绍这个API,可参见这里:“ 深入Node.compareDocumentPosition API ”。

例如:

var compareValue = img.compareDocumentPosition(compareImg);
if (compareValue == 2) {
  // compareImg在前
} else if (compareValue == 4) {
  // compareImg在后
} else if (compareValue == 0) {
  // 就是compareImg元素自身
} else {
  // 其它位置关系
}

如果 compareValue2 ,则表示 compareImgimg 的前面;如果是 4 ,则表示 compareImgimg 的后面。

由此我们可以轻松判断点击图片和对比图片之间的文档位置关系,寥寥几行代码的事情。

不过需要注意的是,如果是判断其他非替换元素的位置关系,则不能使用数值比对,因为可能 compareDocumentPosition() 方法执行后的值是一个混合数值,例如:

// 返回值是 10,8 + 2
document.body.compareDocumentPosition(document.documentElement);
// 返回值是 20,16 + 4
document.documentElement.compareDocumentPosition(document.body)

我们需要使用单个 & 符合和对应目标值进行与位运算的结果来判定,例如:

if (document.body.compareDocumentPosition(document.documentElement) & 2) {
   // document.documentElement在document.body前面
   // ...
}

如果不是很理解,可以访问我刚写的专门 深入介绍compareDocumentPosition 的文章。

四、如何判断click元素是图片

例如:

container.onclick = function (event) {
  // event.target ...
}

业界用的比较多的方法是使用tagName值进行判断,如下:

event.target.tagName == 'IMG'  // true或false

所有浏览器都返回大写标签名,当然,如果你不放心(以后变了,或者遇到SB浏览器),可以更严格比对下:

/^img$/i.test(event.target.tagName)    // true或false
event.target.tagName.toLowerCase() == 'img'    // true或false

我们还可以使用nodeName进行判断,例如:

event.target.nodeName == 'IMG'    // true或false

最后,在介绍一种对象类型判断方法,如下:

event.target instanceof Image    // true或false

也是可以的。

五、答疑要点总结

  1. 包含关系推荐使用 Node.contains() 方法;
  2. 判断当前元素是否是IMG,可以 :
    event.target.tagName/nodeName == 'IMG'
    /^img$/i.test(event.target.tagName)
    event.target.tagName.toLowerCase() == 'img'
    event.target instanceof Image
  3. 前后节点关系判断使用 Node.compareDocumentPosition()

关于直播答疑

每周三会在这个 项目issues 中发布小测题,依次CSS,JS和DOM,每周六上午10:00~11:00之间直播答疑。

有兴趣参与的可以多多关注下。

以上~

Mfm2U3B.png!web

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

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

(本篇完)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK