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

这篇文章发布于 2019年03月2日,星期六,20:52,归类于 JS API。 阅读 18639 次, 今日 3 次 7 条评论

 

小测头图

一、题目与考察点

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

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

DOM小测28期题目

本题主要考察如何判断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);

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

控制台输出结果如下:

iframe内外节点包含关系

如果想要知道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之间直播答疑。

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

以上~

(本篇完)

分享到:


发表评论(目前7 条评论)

  1. saber说道:

    学到了~

  2. 蜗牛行者说道:

    学习了

  3. 我又来了一次说道:

    日常支持大佬

  4. 飞飞酱说道:

    学习了,感谢鑫神

  5. misshoya说道:

    想进群~

  6. 慧慧说道:

    每周六上午10:00~11:00之间的直播上哪里去看

  7. test说道:

    点个赞