一、题目与考察点
题目内容如下(点击查看大图):

本题主要考察如何判断DOM节点文档前后位置,父子关系等。我看了下最后的回答,近9成的回答使用了非常啰嗦的方法,比例之高,实在出乎意料。实际上,本题有非常简单、寥寥数行就能实现的方法,只要你知道下面这两个很有用的DOM原生API,一个是contains()方法,判断DOM元素或节点是否有包含关系;另外一个是compareDocumentPosition()方法,更强悍的DOM或节点位置关系判断,无论是前后、内外还是跨文档都可以。
本次B站答疑直播在周六上午10:34分开始,持续约30分钟,有录播,可以点击这里观看,建议1.5倍速食用。
二、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内外的包含关系,则需要使用另外的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 {
// 其它位置关系
}如果compareValue是2,则表示compareImg在img的前面;如果是4,则表示compareImg在img的后面。
由此我们可以轻松判断点击图片和对比图片之间的文档位置关系,寥寥几行代码的事情。
不过需要注意的是,如果是判断其他非替换元素的位置关系,则不能使用数值比对,因为可能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或falseevent.target.tagName.toLowerCase() == 'img' // true或false我们还可以使用nodeName进行判断,例如:
event.target.nodeName == 'IMG' // true或false最后,在介绍一种对象类型判断方法,如下:
event.target instanceof Image // true或false也是可以的。
五、答疑要点总结
- 包含关系推荐使用
Node.contains()方法; - 判断当前元素是否是IMG,可以 :
event.target.tagName/nodeName == 'IMG'/^img$/i.test(event.target.tagName)event.target.tagName.toLowerCase() == 'img'event.target instanceof Image - 前后节点关系判断使用
Node.compareDocumentPosition()。
关于直播答疑
每周三会在这个项目的issues中发布小测题,依次CSS,JS和DOM,每周六上午10:00~11:00之间直播答疑。
有兴趣参与的可以多多关注下,免费的。
以上~
