網站搜尋

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 及以下版本不支援。