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.

AJ
źródło
3
Powinieneś użyć stackoverflow.com/a/18234150/1868545
llange
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;
}
Asaf
źródło
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

Brian Di Palma
źródło
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
}
GitaarLAB
źródło
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
}
Josh Crozier
źródło
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.


źródło
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,
16

Możesz użyć metody zawiera

var result = parent.contains(child);

lub możesz spróbować użyć CompareDocumentPosition ()

var result = nodeA.compareDocumentPosition(nodeB);

Ostatni jest bardziej wydajny: w wyniku zwraca maskę bitów.

Iegor Kozakov
źródło
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;
}
RashFlash
źródło
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";
}
użytkownik3675072
źródło
-2

Ostatnio natknąłem się na tę funkcję, która może:

Node.compareDocumentPosition ()

ZioCain
źródło
2
Odebrano, ponieważ ta sama odpowiedź istniała już wiele razy i zawierała więcej informacji niż tylko link zewnętrzny.
Stefan Fabian