Як перевірити Javascript, чи міститься один елемент в іншому


201

Як я можу перевірити, чи один елемент DOM є дочірнім іншим елементом DOM? Чи є вбудовані методи для цього? Наприклад, щось на кшталт:

if (element1.hasDescendant(element2)) 

або

if (element2.hasParent(element1)) 

Якщо ні, то будь-які ідеї, як це зробити? Він також повинен бути перехресним браузером. Я також повинен зазначити, що дитина може вкладатись на багато рівнів нижче батьківських.


3
Ви повинні використовувати stackoverflow.com/a/18234150/1868545
llange

Я вважаю, що як JS сьогодні є "живим стандартом", прийняту відповідь слід повторно перевірити, щоб вказати метод "conatins" як прийняту відповідь.
DavidTaubmann

Відповіді:


219

Оновлення: Зараз існує рідний спосіб досягти цього. 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;
}

Дякую за вашу відповідь, проблема полягає в тому, що дитина може вкладатись на багато рівнів нижче батьків.
AJ.

@AJ: Я оновив свою відповідь, щоб включити рішення, яке повинно працювати для довільного введення дитини у батьків.
Асаф

228
Якщо хтось зараз до цього звертається, можливо, ви можете використовувати Node.contains ( developer.mozilla.org/en-US/docs/DOM/Node.contains ), яка є власною функцією в сучасних браузерах.
Адам Хіт

2
@JohnCarrell Є Node.contains(хоч сторінки каніуси немає)
gcampbell

3
@Asaph Чи можете ви оновити відповідь, щоб включити нову Node.contains? :)

355

Вам слід скористатися Node.contains, оскільки він тепер стандартний і доступний у всіх браузерах.

https://developer.mozilla.org/en-US/docs/Web/API/Node.contains


Метод Node.hasParent (батьківський) є непотрібним, але це буде Node.prototype.hasParent = функція (елемент) {return element.contains (this);};
фланж

6
Чи підтримується це також у мобільних браузерах? Документи mdn мають знаки запитання для Android, Safari, IE та Opera.
thetallweeks

1
@thetallweeks - працює як мінімум на моєму Android 7.
Sphinxxx

5
ви могли принаймні додати приклад
Nino Škopac

2
Це гарний одяг, але приклад був би дуже приємний:var parent = document.getElementById('menu'); var allElements = document.getElementsByTagName('a'); if (parent.contains(allElements[i]) { alert('Link is inside meni'); }
baron_bartek

45

Мені просто довелося поділитися "моїм".

Хоча концептуально такий самий, як відповідь Асафа (вигідна однакова сумісність між браузерами, навіть 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 оцінюється до тих пір, поки оцінюється умова whiletrue.
Оператор&&(AND) повертає булеве значення true / false після оцінки лівої та правої частини, але лише в тому випадку, якщо лівий бік був істинним ( left-hand && right-hand) .

Ліва сторона (з &&) має вигляд : (c=c.parentNode).
Це перший буде призначити parentNodeз cдо , cа потім оператор І буде оцінювати отриманий cяк логічне значення.
Оскільки parentNodeповертається, nullякщо не залишилося жодного з батьків, і nullперетворюється на нього false, цикл while буде правильно зупинятися, коли немає більше батьків.

Права (з &&) має вигляд : c!==p.
Оператор !==порівняння " не зовсім дорівнює". Отже, якщо батько дитини не є батьком (ви вказали), який він оцінює true, але якщо батько дитини є батьком, то він оцінює false.
Отже, якщо c!==p оцінюється як false, тоді &&оператор повертається falseяк умова while, а цикл while припиняється. (Зверніть увагу, що немає необхідності в тілі "час", і ;крапка з комою потрібна.)

Отже, коли цикл while закінчується, cце або вузол (не null), коли він знайшов батьківський АБО він є null(коли цикл пробіг до кінця, не знайшовши відповідності).

Таким чином , ми просто returnтой факт (конвертується в логічне значення, замість вузла) з: return !!c;: від !( NOTоператор) інвертує логічне значення ( trueстає falseі навпаки).
!cперетворює c(вузол або нуль) в булеве значення, перш ніж воно зможе інвертувати це значення. Тож додавання секунди !( !!c) перетворює цю помилкову назад у справжню (саме тому подвійний !!часто використовується для 'перетворення будь-чого на булевий').


Додатково:
Тіло / корисне навантаження функції настільки мало, що залежно від випадку (наприклад, коли воно не використовується часто і з’являється лише один раз у коді), можна навіть опустити функцію (обгортання) та просто використовувати цикл 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
@SolomonUcko: Суворе порівняння рівності ( !==) може підвищити швидкість, даючи зрозуміти компілятору, що він може пропускати перевірку типу та необов'язкові неявні етапи перетворення, які відбуватимуться у порівнянні з рівним рівнем , тим самим покращуючи швидкість (більш точно описуючи, що ми хочуть, щоб це сталося, що також корисно програмісту). Я також нагадаю, коли я писав це, що просто !=нестабільне порівняння ( ) було або дуже схильним до помилок, або взагалі не працювало в деяких старих браузерах (я підозрюю, що це було в IE6, але я забув ).
GitaarLAB

29

Ще одне рішення, яке не згадувалося:

Приклад тут

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
}

19

Погляньте на 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.

Не підтримується в IE <= 8.


цікаво! Це нагадує мені про функцію JS, яку я розробив у 2003 році ... Взяв у мене кілька днів і звернувся за допомогою до розробників JS ... Неймовірно (і сумно), що на сьогоднішній день все ще немає повного перетягування або об'єкта, повного впровадження браузера. .
DavidTaubmann


2

Я натрапив на чудовий фрагмент коду, щоб перевірити, чи є елемент дочірнього іншого елемента. Я повинен використовувати це, тому що IE не підтримує .containsметод елемента. Сподіваюся, що це допоможе і іншим.

Нижче наведено функцію:

function isChildOf(childObject, containerObject) {
  var returnValue = false;
  var currentObject;

  if (typeof containerObject === 'string') {
    containerObject = document.getElementById(containerObject);
  }
  if (typeof childObject === 'string') {
    childObject = document.getElementById(childObject);
  }

  currentObject = childObject.parentNode;

  while (currentObject !== undefined) {
    if (currentObject === document.body) {
      break;
    }

    if (currentObject.id == containerObject.id) {
      returnValue = true;
      break;
    }

    // Move up the hierarchy
    currentObject = currentObject.parentNode;
  }

  return returnValue;
}

Я буквально спробував будь-який інший поліфайл на цій сторінці, включаючи .contain, і це єдиний, хто працював. Дякую!
protoEvangelion

1

спробуйте це:

x = document.getElementById("td35");
if (x.childElementCount > 0) {
    x = document.getElementById("LastRow");
    x.style.display = "block";
}
else {
    x = document.getElementById("LastRow");
    x.style.display = "none";
}

-2

Нещодавно я натрапив на цю функцію, яка може:

Node.compareDocumentPosition ()


2
Незважаючи на те, що однакова відповідь існувала вже кілька разів, отримавши більше інформації, ніж просто зовнішнє посилання.
Стефан Фабіян
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.