So überprüfen Sie in Javascript, ob ein Element in einem anderen enthalten ist

201

Wie kann ich überprüfen, ob ein DOM-Element einem anderen DOM-Element untergeordnet ist? Gibt es dafür eingebaute Methoden? Zum Beispiel so etwas wie:

if (element1.hasDescendant(element2)) 

oder

if (element2.hasParent(element1)) 

Wenn nicht, dann irgendwelche Ideen, wie das geht? Es muss auch browserübergreifend sein. Ich sollte auch erwähnen, dass das Kind viele Ebenen unterhalb des Elternteils verschachtelt sein könnte.

AJ.
quelle
Ich glaube, da JS heutzutage ein "Live-Standard" ist, sollte die akzeptierte Antwort erneut überprüft werden, um die "Conatins" -Methode als akzeptierte Antwort anzugeben.
DavidTaubmann

Antworten:

219

Update: Es gibt jetzt einen nativen Weg, um dies zu erreichen. Node.contains(). Erwähnt im Kommentar und unten auch Antworten.

Alte Antwort:

Die Verwendung der parentNodeEigenschaft sollte funktionieren. Es ist auch aus browserübergreifender Sicht ziemlich sicher. Wenn bekannt ist, dass die Beziehung eine Ebene tief ist, können Sie sie einfach überprüfen:

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

Wenn das Kind beliebig tief im Elternteil verschachtelt werden kann, können Sie eine ähnliche Funktion wie die folgende verwenden, um die Beziehung zu testen:

function isDescendant(parent, child) {
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}
Asaph
quelle
Vielen Dank für Ihre Antwort. Das Problem ist, dass das Kind viele Ebenen unter dem Elternteil verschachtelt sein kann.
AJ.
@AJ: Ich habe meine Antwort aktualisiert, um eine Lösung aufzunehmen, mit der das Kind willkürlich tief im Elternteil verschachtelt werden kann.
Asaph
228
Wenn jetzt jemand dazu kommt, können Sie möglicherweise Node.contains ( developer.mozilla.org/en-US/docs/DOM/Node.contains ) verwenden, eine native Funktion in modernen Browsern.
Adam Heath
2
@ JohnCarrell Es gibt Node.contains(keine caniuse Seite obwohl)
gcampbell
3
@Asaph Könnten Sie bitte Ihre Antwort aktualisieren, um die neue aufzunehmen Node.contains? :)
355

Sie sollten verwenden Node.contains, da es jetzt Standard ist und in allen Browsern verfügbar ist.

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

Brian Di Palma
quelle
Die Node.hasParent (übergeordnete) Methode ist nicht erforderlich, wäre jedoch Node.prototype.hasParent = function (element) {return element.contains (this);};
llange
6
Wird es auch in mobilen Browsern unterstützt? Die MDN-Dokumente haben Fragezeichen für Android, Safari, IE und Opera.
ganzen Wochen
1
@thetallweeks - Funktioniert mindestens auf meinem Android 7.
Sphinxxx
5
Sie hätten zumindest ein Beispiel hinzufügen können
Nino Škopac
2
Dies ist eine gute Antwort, aber Beispiel wäre wirklich schön:var parent = document.getElementById('menu'); var allElements = document.getElementsByTagName('a'); if (parent.contains(allElements[i]) { alert('Link is inside meni'); }
baron_bartek
45

Ich musste nur 'meine' teilen.

Obwohl es konzeptionell mit der Antwort von Asaph identisch ist (mit der gleichen browserübergreifenden Kompatibilität, sogar mit IE6), ist es viel kleiner und praktisch, wenn die Größe knapp ist und / oder wenn es nicht so oft benötigt wird.

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

..oder als Einzeiler ( nur 64 Zeichen !):

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

und jsfiddle hier .


Verwendung:
childOf(child, parent) Gibt booleantrue| zurück false.

Erläuterung: Wird
while ausgewertet, solange die while-Bedingung ausgewertet wirdtrue.
Der&&Operator (AND) gibt diesen booleschen Wert true / false zurück, nachdem die linke und die rechte Seite ausgewertet wurden, jedoch nur, wenn die linke Seite true ( left-hand && right-hand) war. .

Die linke Seite (von &&) ist : (c=c.parentNode).
Dadurch wird zuerst das parentNodevon cbis zugewiesen , cund dann bewertet der AND-Operator das Ergebnis cals Booleschen Wert.
Da parentNodezurückgegeben wird, nullwenn kein übergeordnetes Element mehr vorhanden ist und in nulldas konvertiert wird, falsewird die while-Schleife korrekt gestoppt, wenn keine übergeordneten Elemente mehr vorhanden sind.

Die rechte Seite (von &&) ist : c!==p.
Der !==Vergleichsoperator ist ' nicht genau gleich'. Wenn also das Elternteil des Kindes nicht das Elternteil ist (das Sie angegeben haben), wird es ausgewertet true, aber wenn das Elternteil des Kindes das Elternteil ist, wird es ausgewertet false.
Also , wenn c!==p das Ergebnis falsch, dann werden die &&Bediener kehrt falseals while-Zustand und die while-Schleife beendet. (Beachten Sie, dass ein while-body und das Schließen nicht erforderlich sind; erforderlich ist Semikolon erforderlich ist.)

Wenn die while-Schleife endet, cist sie entweder ein Knoten (nicht null), wenn sie ein übergeordnetes Element gefunden hat, oder sie ist esnull (wenn die Schleife bis zum Ende durchgegangen ist, ohne eine Übereinstimmung zu finden).

Also einfach returndiese Tatsache (konvertiert als boolescher Wert anstelle des Knotens) mit :: return !!c;der !( NOTOperator) invertiert einen booleschen Wert ( truewird falseund umgekehrt).
!ckonvertiert c(Knoten oder Null) in einen Booleschen Wert, bevor dieser Wert invertiert werden kann. Wenn Sie also ein second !( !!c) hinzufügen, wird dieses false wieder in true konvertiert (weshalb ein Double !!häufig verwendet wird, um "alles in Boolesche Werte umzuwandeln").


Extra:
Die Körper / Nutzlast - Funktion ist so klein , dass im Fall abhängig (wie wenn es nicht oft verwendet wird , und erscheint nur einmal im Code), einer konnte sogar die Funktion (Verpackung) auslassen und benutzen Sie einfach die while-Schleife:

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
}

anstatt:

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
quelle
4
@SolomonUcko: Der strikte Gleichheitsvergleich ( !==) kann die Geschwindigkeit verbessern, indem dem Compiler klar gemacht wird, dass er die Typprüfung und optionale implizite Konvertierungsschritte überspringen kann, die bei einem losen Gleichheitsvergleich auftreten würden, wodurch die Geschwindigkeit verbessert wird (indem genauer beschrieben wird, was wir tun passieren wollen, was auch für den Programmierer von Vorteil ist). Ich erinnere mich auch daran, als ich dies schrieb, dass nur der lose Vergleich ( !=) entweder sehr fehleranfällig war oder in einigen älteren Browsern überhaupt nicht funktionierte (ich vermute, dass es in IE6 war, aber ich habe es vergessen ).
GitaarLAB
29

Eine andere Lösung, die nicht erwähnt wurde:

Beispiel hier

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

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

Es spielt keine Rolle, ob das Element ein direktes untergeordnetes Element ist, es funktioniert in jeder Tiefe.


Alternativ mit der .contains()Methode :

Beispiel hier

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

if (parent.contains(child)) {
    // .. it's a child
}
Josh Crozier
quelle
19

Schauen Sie sich Node # compareDocumentPosition an .

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

Andere Beziehungen sind DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_PRECEDINGundDOCUMENT_POSITION_FOLLOWING .

Wird im IE <= 8 nicht unterstützt.


quelle
interessant! Dies erinnert mich an eine JS-Funktion, die ich im Jahr 2003 entwickelt habe ... Ich habe einige Tage gebraucht und Hilfe von einigen JS-Entwicklern erhalten ... Unglaublich (und traurig), dass es heutzutage immer noch keine vollständige Drag-Drop- oder objektangepasste vollständige Browser-Implementierung gibt .
DavidTaubmann
16

Sie können die Methode includes verwenden

var result = parent.contains(child);

oder Sie können versuchen, compareDocumentPosition () zu verwenden

var result = nodeA.compareDocumentPosition(nodeB);

Der letzte ist leistungsfähiger: Als Ergebnis wird eine Bitmaske zurückgegeben.

Iegor Kozakov
quelle
2

Ich bin auf einen wunderbaren Code gestoßen, um zu überprüfen, ob ein Element ein Kind eines anderen Elements ist oder nicht. Ich muss dies verwenden, da der IE die .containsElementmethode nicht unterstützt . Hoffe, das wird auch anderen helfen.

Unten ist die Funktion:

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
quelle
Ich habe buchstäblich jede andere Polyfüllung auf dieser Seite ausprobiert, einschließlich .contains, und dies ist die einzige, die funktioniert hat. Vielen Dank!
ProtoEvangelion
1

Probier diese:

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
quelle
-2

Ich bin kürzlich auf diese Funktion gestoßen, die Folgendes bewirken könnte:

Node.compareDocumentPosition ()

ZioCain
quelle
2
Downvoted, da dieselbe Antwort bereits mehrmals mit mehr Informationen als nur einem externen Link vorhanden war.
Stefan Fabian