Jak sprawdzić w Javascript, czy jeden element jest zawarty w innym


201

Jak mogę sprawdzić, czy jeden element DOM jest dzieckiem innego elementu DOM? Czy istnieją jakieś wbudowane metody do tego? Na przykład coś takiego:

if (element1.hasDescendant(element2)) 

lub

if (element2.hasParent(element1)) 

Jeśli nie to jakieś pomysły, jak to zrobić? Musi to również być przeglądarka. Powinienem również wspomnieć, że dziecko może być zagnieżdżone na wielu poziomach poniżej rodzica.



Uważam, że ponieważ JS jest obecnie „standardem na żywo”, należy ponownie sprawdzić zaakceptowaną odpowiedź, aby podać metodę „conatins” jako przyjętą odpowiedź.
DavidTaubmann

Odpowiedzi:


219

Aktualizacja: Istnieje teraz natywny sposób na osiągnięcie tego. Node.contains(). Wspomniany w komentarzu i poniżej również odpowiedzi.

Stara odpowiedź:

Korzystanie z parentNodewłaściwości powinno działać. Jest to również całkiem bezpieczne z punktu widzenia różnych przeglądarek. Jeśli wiadomo, że związek ma głębokość jednego poziomu, możesz to sprawdzić po prostu:

if (element2.parentNode == element1) { ... }

Jeśli dziecko może zostać dowolnie zagnieżdżone głęboko w rodzicu, możesz użyć funkcji podobnej do poniższej, aby przetestować relację:

function isDescendant(parent, child) {
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}

Dzięki za odpowiedź problem polega na tym, że dziecko może być zagnieżdżone wiele poziomów poniżej rodzica.
AJ.

@AJ: Zaktualizowałem swoją odpowiedź, aby uwzględnić rozwiązanie, które powinno działać w celu zagnieżdżenia dziecka dowolnie głęboko w rodzicu.
Asaph

228
Jeśli ktoś przyjdzie do tego teraz, może być możliwe użycie Node.contains ( developer.mozilla.org/en-US/docs/DOM/Node.contains ), która jest rodzimą funkcją współczesnych przeglądarek.
Adam Heath

2
@JohnCarrell Jest Node.contains(nie ma strony caniuse)
gcampbell 25.09.16

3
@Asaph Czy możesz zaktualizować swoją odpowiedź, aby uwzględnić nową Node.contains? :)

355

Powinieneś użyć Node.contains, ponieważ jest teraz standardowy i dostępny we wszystkich przeglądarkach.

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


Metoda Node.hasParent (rodzic) jest niepotrzebna, ale byłaby to Node.prototype.hasParent = funkcja (element) {return element.contains (this);};
llange

6
Czy jest również obsługiwany w przeglądarkach mobilnych? Dokumenty MDN mają znaki zapytania dla Androida, Safari, IE i Opery.
thetallweeks

1
@thetallweeks - Działa przynajmniej na moim Androidzie 7.
Sphinxxx

5
mógłbyś przynajmniej dodać przykład
Nino Škopac

2
To dobre odpowiedzi, ale przykład byłby naprawdę fajny:var parent = document.getElementById('menu'); var allElements = document.getElementsByTagName('a'); if (parent.contains(allElements[i]) { alert('Link is inside meni'); }
baron_bartek

45

Po prostu musiałem podzielić się „moim”.

Chociaż koncepcyjnie to samo co odpowiedź Asafa (korzystająca z tej samej kompatybilności między przeglądarkami, nawet IE6), jest o wiele mniejsza i przydaje się, gdy rozmiar jest na wagę złota i / lub gdy nie jest tak często potrzebny.

function childOf(/*child node*/c, /*parent node*/p){ //returns boolean
  while((c=c.parentNode)&&c!==p); 
  return !!c; 
}

.. lub jako jedna linijka ( tylko 64 znaki !):

function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}

i jsfiddle tutaj .


Użycie:
childOf(child, parent) zwraca wartość logicznątrue| false.

Objaśnienie:
while ocenia tak długo, jak ocenia warunek whiletrue.
Operator&&(AND) zwraca wartość logiczną „prawda / fałsz” po ocenie lewej i prawej strony, ale tylko wtedy, gdy po lewej stronie była prawda ( left-hand && right-hand) .

Strona lewa (z &&) wynosi: (c=c.parentNode).
Będzie to pierwszy przypisać parentNodeod cdo c, a następnie operator AND oceni uzyskany cjako wartość logiczną.
Ponieważ parentNodezwraca, nulljeśli nie ma już rodzica i nulljest konwertowany false, pętla while poprawnie zatrzyma się, gdy nie będzie już rodziców.

Z boku po prawej stronie (z &&) wynosi: c!==p. Operator porównania jest „ nie dokładnie równa”. Więc jeśli rodzic dziecka nie jest rodzicem (podałeś), to ocenia , ale jeśli rodzic dziecka jest rodzicem, to ocenia . Jeśli więc zostanie ustawiona wartość false, operator powróci jako warunek while, a pętla while zatrzyma się. (Zauważ, że nie ma potrzeby używania ciała chwilowego i wymagany jest średnik zamykający .)
!==truefalse
c!==p&&false;

Więc kiedy pętla while kończy się, cjest albo węzłem (nie null), kiedy znajduje nadrzędny OR, albo jest null(kiedy pętla biegnie do końca bez znalezienia dopasowania).

W ten sposób tylko returnfakt (konwertowane wartości logicznej, natomiast węzła) o: return !!c;: z !( NOToperator) odwraca wartość logiczną ( truestaje się falsei vice versa).
!ckonwertuje c(węzeł lub null) na wartość logiczną, zanim będzie mógł odwrócić tę wartość. Tak więc dodanie second !( !!c) konwertuje to fałsz z powrotem na true (dlatego podwójne !!jest często używane do „konwersji wszystkiego na boolean”).


Dodatkowo:
treść / ładunek funkcji jest tak mały, że w zależności od wielkości liter (np. Kiedy nie jest często używany i pojawia się tylko raz w kodzie), można nawet pominąć funkcję (zawijanie) i po prostu użyć pętli 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
}

zamiast:

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: ścisłe porównanie równości ( !==) może poprawić prędkość, dając kompilatorowi do zrozumienia, że ​​może pominąć sprawdzanie typu i opcjonalne niejawne kroki konwersji, które wystąpiłyby w luźnym porównaniu równości, a tym samym poprawić prędkość (dokładniej opisując, co chcesz się wydarzyć, co jest również korzystne dla programisty). Wydaje mi się również, że pamiętam z czasu, gdy to napisałem, że samo luźne porównanie ( !=) było albo bardzo podatne na błędy, albo w ogóle nie działało w niektórych starszych przeglądarkach (podejrzewam, że było w IE6, ale zapomniałem ).
GitaarLAB,

29

Kolejne rozwiązanie, o którym nie wspomniano:

Przykład tutaj

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

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

Nie ma znaczenia, czy element jest bezpośrednim dzieckiem, będzie działał na dowolnej głębokości.


Alternatywnie, stosując .contains()metodę :

Przykład tutaj

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

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

19

Spójrz na 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;
}

Inne związki obejmują DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_PRECEDINGi DOCUMENT_POSITION_FOLLOWING.

Nieobsługiwany w IE <= 8.


ciekawy! Przypomina mi to funkcję JS, którą opracowałem w 2003 roku ... Zajęło mi to kilka dni i otrzymałem pomoc od niektórych programistów JS ... Niesamowite (i smutne), że w dzisiejszych czasach wciąż nie ma pełnego przeciągania i implementacji obiektowej pełnej przeglądarki .
DavidTaubmann,


2

Natknąłem się na wspaniały kawałek kodu, aby sprawdzić, czy element jest dzieckiem innego elementu. Muszę tego użyć, ponieważ IE nie obsługuje .containsmetody element. Mam nadzieję, że pomoże to również innym.

Poniżej znajduje się funkcja:

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

Dosłownie wypróbowałem wszystkie inne wypełnienia na tej stronie, w tym .contains, i tylko to zadziałało. Dzięki!
protoEvangelion

1

Spróbuj tego:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.