DOM - 如何在 Javascript 中檢查一個元素是否被包含在另一個元素中
我如何檢查一個 DOM 元素是否是另一個 DOM 元素的子元素?有沒有內建的方法可以做到這個?
例如,像這樣檢查 element2 是否包含在 element1 中:
if (element1.hasDescendant(element2))
或者如果 element2 的父元素/祖父元素/等等是 element1:
if (element2.hasAncestor(element1))
如果不是,那有什麼方法可以做到這個嗎?它還需要跨瀏覽器相容。我還應該提到,子元素可能在父元素的很多層級下。
答案#1
你應該使用 Node.contains,因為它現在是標準且所有瀏覽器都可使用。
https://developer.mozilla.org/zh-CN/docs/Web/API/Node.contains
const parent = document.getElementById("my-parent")
const child = document.getElementById("my-child")
parent.contains(child)
一個「hasParent」函數的範例,它會返回是否有任何元素是父元素:
const hasParent = (element, ...parents) => parents.some((parent) => parent.contains(element))
hasParent(child, parent1, parent2, parent3)
答案#2
更新: 現在有一種原生方法可以實現這個功能。Node.contains()。在評論和下面的答案中也有提到。
舊答案:
使用 parentNode 屬性應該可行。從跨瀏覽器的角度來看,這也相當安全。如果已知層級關係只有一層深,你可以簡單地檢查它:
if (element2.parentNode == element1) { ... }
如果子項可以任意深度地嵌套在父項內,您可以使用類似以下的函數來測試這種關係:
function isDescendant(parent, child) {
var node = child.parentNode;
while (node != null) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
答案#3
我只是想分享『我的』。
雖然在概念上與 Asaph 的回答相同(受益於相同的跨瀏覽器相容性,即使是 IE6),它卻小得多,當空間有限或不需要經常使用時非常方便。
function childOf(/*child node*/c, /*parent node*/p){ //returns boolean
while((c=c.parentNode)&&c!==p);
return !!c;
}
..或作為單行程式碼 (僅 64 個字元!):
function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}
以及這裡的 jsfiddle。
用法:
childOf(child, parent) 返回布林值 true|false。
說明:
while 會在 while 條件判斷為 true 時持續評估。
&&(AND)運算子會在評估左邊與右邊之後回傳布林值 true/false,但只有在左邊為 true 時才會評估右邊(left-hand && right-hand)。
左側(&&)是:(c=c.parentNode)。
這將首先把 c 的 parentNode 賦值給 c,然後 AND 運算符會將結果 c 作為布林值來計算。
由於如果沒有父節點,parentNode 會返回 null,而 null 會被轉換為 false,因此當沒有更多父節點時,while 迴圈將正確停止。
右邊(與&)為:c!==p。
!== 比較運算子的「不完全等於」。所以如果子的父母不是你指定的父,它會被評估為真;但如果子的父母是父母,則會被評估為假。
因此,如果 c!==p 被判定為假,則 & & 運算子回傳為假,因為 while-條件與 while-loop 停止。(注意不需要 while-body 和閉合;分號是必須的。)
所以當 while 迴圈結束時,c 要麼是一個節點(不是 null),表示它找到了父節點,要麼是 null(當迴圈執行到最後仍未找到匹配項時)。
因此,我們只是簡單地用 return 返回這個事實(以布林值的形式,而不是節點)如下:return !!c;:!(NOT 運算符)會反轉布林值(true 變為 false,反之亦然)。
!c 將 c(節點或 null)先轉換為布林值,然後再反轉該值。所以再加上一個 !(!!c)會將這個 false 再轉回 true(這就是為什麼雙 !! 常用來“將任何東西轉換為布林值”)。
額外:
函數的主體/內容非常小,以至於根據情況(例如當它不常被使用且在程式碼中只出現一次時),甚至可以省略函數(包裝),直接使用 while 迴圈:
var a=document.getElementById('child'),
b=document.getElementById('parent'),
c;
c=a; while((c=c.parentNode)&&c!==b); //c=!!c;
if(!!c){ //`if(c)` if `c=!!c;` was used after while-loop above
//do stuff
}
而不是:
var a=document.getElementById('child'),
b=document.getElementById('parent'),
c;
function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}
c=childOf(a, b);
if(c){
//do stuff
}
答案#4
另一個沒有提到的解決方案:
範例在此
var parent = document.querySelector('.parent');
if (parent.querySelector('.child') !== null) {
// .. it's a child
}
無論該元素是否為直接子元素都無關緊要,它在任何深度都能運作。
或者,使用 .contains() 方法:
範例在此
var parent = document.querySelector('.parent'),
child = document.querySelector('.child');
if (parent.contains(child)) {
// .. it's a child
}
答案#5
你可以使用 contains 方法
var result = parent.contains(child);或者你可以嘗試使用 compareDocumentPosition()
var result = nodeA.compareDocumentPosition(nodeB);最後一個更強大:它會返回一個位掩碼作為結果。
答案#6
看看 Node#compareDocumentPosition。
function isDescendant(ancestor,descendant){
return ancestor.compareDocumentPosition(descendant) &
Node.DOCUMENT_POSITION_CONTAINS;
}
function isAncestor(descendant,ancestor){
return descendant.compareDocumentPosition(ancestor) &
Node.DOCUMENT_POSITION_CONTAINED_BY;
}
其他關係包括 DOCUMENT_POSITION_DISCONNECTED、DOCUMENT_POSITION_PRECEDING 和 DOCUMENT_POSITION_FOLLOWING。
在 IE8 及以下版本不支援。
