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

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

if (element1.hasDescendant(element2)) 

или

if (element2.hasParent(element1)) 

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

Ответ 1

Использование свойства 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 после оценки левой и правой частей, но только если левая часть была истинной (left-hand && right-hand).

Левая часть (of &&): (c=c.parentNode).
Это сначала назначит parentNode c для c а затем оператор AND оценит результирующий c как логическое значение.
Так как parentNode возвращает null если родительского parentNode не осталось, а 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

Другое решение, о котором не упоминалось:

Пример здесь

var parent = document.querySelector('.parent');

if (parent.querySelector('.child') !== null) {
    // .. it a child
}

Не имеет значения, является ли элемент прямым ребенком, он будет работать на любой глубине.


Альтернативно, используя метод .contains():

Пример здесь

var parent = document.querySelector('.parent'),
    child = document.querySelector('.child');

if (parent.contains(child)) {
    // .. it a child
}

Ответ 5

Посмотрите 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.

Ответ 6

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

var result = parent.contains(child);

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

var result = nodeA.compareDocumentPosition(nodeB);

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

Ответ 7

Попробуй это:

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";
}

Ответ 8

Я наткнулся на замечательный кусок кода, чтобы проверить, является ли элемент дочерним по отношению к другому элементу. Я должен использовать это, потому что 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;
}

Ответ 9

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

Node.compareDocumentPosition()