判断节点之间的关系及根据节点关系查找节点
背景
在某些情况下,可能需要判断两节点间的关系,例如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(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)