Как проверить в Javascript, содержится ли один элемент в другом

202

Как я могу проверить, является ли один элемент DOM дочерним по отношению к другому элементу DOM? Есть ли встроенные методы для этого? Например, что-то вроде:

if (element1.hasDescendant(element2)) 

или

if (element2.hasParent(element1)) 

Если нет, то есть идеи, как это сделать? Это также должно быть кросс-браузер. Я должен также упомянуть, что ребенок может быть вложен на много уровней ниже родительского.

AJ.
источник
3
Вы должны использовать stackoverflow.com/a/18234150/1868545
llange
Я считаю, что, поскольку JS в настоящее время является «живым стандартом», принятый ответ должен быть перепроверен, чтобы указать «conatins» метод в качестве принятого ответа.
Дэвид Таубманн

Ответы:

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(хотя нет страницы caniuse)
gcampbell
3
@Asaph Не могли бы вы обновить свой ответ, чтобы включить новый Node.contains? :)
355

Вы должны использовать Node.contains, так как теперь это стандартно и доступно во всех браузерах.

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

Брайан Ди Пальма
источник
Метод Node.hasParent (parent) не нужен, но будет Node.prototype.hasParent = function (element) {return element.contains (this);};
язык
6
Это также поддерживается в мобильных браузерах? Документы MDN имеют вопросительные знаки для Android, Safari, IE и Opera.
Thetallweeks
1
@thetallweeks - работает на моем Android 7 по крайней мере.
Sphinxxx
5
Вы могли бы хотя бы добавить пример
Нино Шкопац
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 нет необходимости, и ;требуется точка с запятой.)

Таким образом, когда цикл while завершается, cэто либо узел (не null), когда он нашел родителя, ИЛИ null(если цикл прошел до конца, не найдя соответствия).

Таким образом , мы просто returnтот факт (конвертируется в логическое значение, вместо узла) с: return !!c;: от !( NOTоператор) инвертирует логическое значение ( trueстановится falseи наоборот).
!cпреобразует c(узел или ноль) в логическое значение, прежде чем оно сможет инвертировать это значение. Таким образом, добавление second !( !!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
}
GitaarLAB
источник
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 ... Невероятно (и грустно), что в настоящее время до сих пор не существует ни полной перетаскивания, ни полной реализации браузера. ,
Дэвид Таубманн
17

Вы можете использовать метод содержит

var result = parent.contains(child);

или вы можете попробовать использовать compareDocumentPosition ()

var result = nodeA.compareDocumentPosition(nodeB);

Последний более мощный: он возвращает битовую маску в качестве результата.

Егор Козаков
источник
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;
}
RashFlash
источник
Я буквально попробовал все остальные полифиллы на этой странице, включая .contains, и это единственный, который сработал. Спасибо!
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";
}
user3675072
источник
-2

Я недавно сталкивался с этой функцией, которая может делать:

Node.compareDocumentPosition ()

ZioCain
источник
2
Понижено, потому что один и тот же ответ существовал уже много раз и содержал больше информации, чем просто внешняя ссылка.
Стефан Фабиан