判断节点之间的关系及根据节点关系查找节点

背景

在某些情况下,可能需要判断两节点间的关系,例如A节点是否是B节点的祖先节点,A、B节点是否是相邻兄弟节点,或者是根据已有的一个节点去寻找与其有某种关系的节点,例如寻找A节点的祖先节点中具有某className的节点。

实现方案

Element.closest()

Element.closest() 方法用来获取:匹配特定选择器且离当前元素最近的祖先元素(也可以是当前元素本身)。如果匹配不到,则返回 null需要注意的是,这种方法只对元素节点有效。
const targetElement = el.closest(selectors);
这种方法可以用于已知子孙元素节点和祖先节点的选择器,从而查找其祖先元素,或者判断当前元素节点是否有该祖先节点。
if(node.closest('div.isHidden')){ // 如果node的某div祖先节点存在isHidden类名 }

Node.contains()

Node 接口的 contains() 方法返回一个布尔值,表示一个节点是否是给定节点的后代,即该节点本身、其直接子节点(childNodes)、子节点的直接子节点等。
node.contains(otherNode)
这种方法适用于所有Node,不仅限于Element。
需要注意的是,contains 对自身节点调用也是true,即node.contains(node) === true // true

Node.compareDocumentPosition

如果上面的方法都不满足需求,那么可以使用compareDocumentPosition ,它能准确地比较两节点之间的具体关系,甚至可以比较不同文档(document)之间的节点。 compareMask = node.compareDocumentPosition( otherNode )
返回值是一个具有以下值的位掩码:
常量名
十进制值
含义
DOCUMENT_POSITION_DISCONNECTED
1
不在同一文档中
DOCUMENT_POSITION_PRECEDING
2
otherNode 在 node 之前
DOCUMENT_POSITION_FOLLOWING
4
otherNode 在 node 之后
DOCUMENT_POSITION_CONTAINS
8
otherNode 包含 node
DOCUMENT_POSITION_CONTAINED_BY
16
otherNode 被 node 包含
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
32
待定
需要特别注意的是,Node.compareDocumentPosition 返回的是位掩码,因此不能直接使用相等来判断,必须要按位与运算符才能得到正确的结果。
if(node.childNodes[0].compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINS){ // node包含node.childNodes[0] }
这是因为有些情况下,节点之间可能存在多种关系,例如otherNode既包含node,又在文档中位于node的前面,那么返回值就是Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINS ,也就是10 ,直接用相等符号判断得到的就将是无效的结果。
node.compareDocumentPosition(otherNode) === (Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINS)
 

querySelector和querySelectorAll

querySelector API大家应该都很熟悉了,它与Element.closest API相反,主要用于查找子孙元素节点。
querySelector 返回与指定的选择器组匹配的元素的后代的第一个元素,而querySelector 返回所有匹配的子孙节点。
需要注意的是,querySelector方法同样也被定义在Element上,也就是说下面两种方法都是可以的:
document.querySelectorAll(selectors) element.querySelectorAll(selectors)