Comment vérifier en Javascript si un élément est contenu dans un autre


202

Comment puis-je vérifier si un élément DOM est un enfant d'un autre élément DOM? Existe-t-il des méthodes intégrées pour cela? Par exemple, quelque chose comme:

if (element1.hasDescendant(element2)) 

ou

if (element2.hasParent(element1)) 

Si ce n'est pas le cas, des idées sur la façon de procéder? Il doit également être multi-navigateur. Je dois également mentionner que l'enfant peut être imbriqué à plusieurs niveaux sous le parent.



Je crois que comme JS est de nos jours un "standard vivant", la réponse acceptée devrait être revérifiée pour indiquer la méthode des "conatines" comme réponse acceptée.
DavidTaubmann

Réponses:


219

Mise à jour: il existe désormais une méthode native pour y parvenir. Node.contains(). Mentionné également dans les commentaires et les réponses ci-dessous.

Ancienne réponse:

L'utilisation de la parentNodepropriété devrait fonctionner. Il est également assez sûr d'un point de vue multi-navigateur. Si la relation est connue pour être d'un niveau, vous pouvez simplement la vérifier:

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

Si l'enfant peut être imbriqué arbitrairement profondément à l'intérieur du parent, vous pouvez utiliser une fonction similaire à la suivante pour tester la relation:

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

Merci pour votre réponse, le problème est que l'enfant pourrait être imbriqué à plusieurs niveaux sous le parent.
AJ.

@AJ: J'ai mis à jour ma réponse pour inclure une solution qui devrait fonctionner pour imbriquer l'enfant arbitrairement profondément dans le parent.
Asaph

228
Si quelqu'un y arrive maintenant, il peut être possible pour vous d'utiliser Node.contains ( developer.mozilla.org/en-US/docs/DOM/Node.contains ) qui est une fonction native dans les navigateurs modernes.
Adam Heath

2
@JohnCarrell Il y a Node.contains(pas de page caniuse cependant)
gcampbell

3
@Asaph Pourriez-vous s'il vous plaît mettre à jour votre réponse pour inclure le nouveau Node.contains? :)

355

Tu devrais utiliser Node.contains , car il est désormais standard et disponible dans tous les navigateurs.

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


La méthode Node.hasParent (parent) n'est pas nécessaire mais serait Node.prototype.hasParent = function (element) {return element.contains (this);};
llange

6
Est-il également pris en charge dans les navigateurs mobiles? Les documents mdn ont des points d'interrogation pour Android, Safari, IE et Opera.
thetallweeks

1
@thetallweeks - Fonctionne au moins sur mon Android 7.
Sphinxxx

5
vous auriez au moins pu ajouter un exemple
Nino Škopac

2
C'est une bonne réponse mais l'exemple serait vraiment sympa:var parent = document.getElementById('menu'); var allElements = document.getElementsByTagName('a'); if (parent.contains(allElements[i]) { alert('Link is inside meni'); }
baron_bartek

45

Je devais juste partager le «mien».

Bien que conceptuellement identique à la réponse d'Asaph (bénéficiant de la même compatibilité entre les navigateurs, même IE6), il est beaucoup plus petit et s'avère pratique lorsque la taille est limitée et / ou lorsqu'elle n'est pas nécessaire si souvent.

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

..ou en une ligne ( 64 caractères seulement !):

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

et jsfiddle ici .


Utilisation:
childOf(child, parent) retourne booléentrue| false.

Explication:
while évalue tant que la condition while est évaluéetrue.
L'&&opérateur (AND) renvoie ce booléen true / false après avoir évalué le côté gauche et le côté droit, mais uniquement si le côté gauche était vrai ( left-hand && right-hand) .

Le côté gauche (de &&) est: (c=c.parentNode).
Cela affectera d'abord le parentNodeof cà cpuis l'opérateur AND évaluera le résultat ccomme un booléen.
Puisque parentNoderenvoie nulls'il n'y a plus de parent et nullest converti en false, la boucle while s'arrêtera correctement lorsqu'il n'y aura plus de parents.

Le côté droit (de &&) est: c!==p.
L' !==opérateur de comparaison n'est « pas exactement égal à». Donc, si le parent de l'enfant n'est pas le parent (vous avez spécifié), il évalue true, mais si le parent de l'enfant est le parent, il évalue false.
Donc, si la valeur est c!==p false, l' &&opérateur retourne falsecomme condition while et la boucle while s'arrête. (Notez qu'il n'est pas nécessaire d'avoir un corps-tout et que le ;point-virgule de fermeture est requis.)

Ainsi, lorsque la boucle while se termine, cest soit un nœud (pas null) lorsqu'il a trouvé un parent OU il l'est null(lorsque la boucle a parcouru jusqu'à la fin sans trouver de correspondance).

Ainsi , nous avons simplement returnce fait (converti en valeur booléenne, au lieu du nœud) avec: return !!c;: le !( NOTopérateur) Inverse une valeur booléenne ( truedevient falseet vice-versa).
!cconvertit c(nœud ou null) en booléen avant de pouvoir inverser cette valeur. Ainsi , l' ajout d' une seconde !( !!c) convertit ce faux retour à vrai ( ce qui est la raison pour laquelle un double !!est souvent utilisé pour "quoi que ce soit converti en booléen).


Extra:
le corps / la charge utile de la fonction est si petit que, selon le cas (comme quand il n'est pas souvent utilisé et n'apparaît qu'une seule fois dans le code), on pourrait même omettre la fonction (habillage) et utiliser simplement la boucle 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
}

au lieu de:

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: La comparaison d'égalité stricte ( !==) peut améliorer la vitesse en indiquant clairement au compilateur qu'elle peut ignorer la vérification de type et les étapes de conversion implicites facultatives qui se produiraient dans une comparaison d'égalité lâche, améliorant ainsi la vitesse (en décrivant plus précisément ce que nous ce qui est également bénéfique pour le programmeur). Je me souviens aussi, lorsque j'ai écrit ceci, que seule la comparaison lâche ( !=) était très sujette aux erreurs ou ne fonctionnait pas du tout dans certains navigateurs plus anciens (je soupçonne que c'était dans IE6, mais j'ai oublié ).
GitaarLAB

29

Une autre solution qui n'a pas été mentionnée:

Exemple ici

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

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

Peu importe que l'élément soit un enfant direct, il fonctionnera à n'importe quelle profondeur.


Alternativement, en utilisant la .contains()méthode :

Exemple ici

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

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

19

Jetez un œil à 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;
}

D' autres relations comprennent DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_PRECEDINGet DOCUMENT_POSITION_FOLLOWING.

Non pris en charge dans IE <= 8.


intéressant! Cela me rappelle une fonction JS que j'ai développée en 2003 ... Cela m'a pris quelques jours et j'ai obtenu l'aide de certains développeurs JS ... Incroyable (et triste) qu'il n'y a toujours pas de mise en œuvre complète du navigateur par glisser-déposer ni objet. .
DavidTaubmann


2

Je suis tombé sur un merveilleux morceau de code pour vérifier si un élément est ou non un enfant d'un autre élément. Je dois l'utiliser car IE ne prend pas en charge la .containsméthode des éléments. J'espère que cela aidera aussi les autres.

Voici la fonction:

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

J'ai littéralement essayé tous les autres polyfill sur cette page, y compris .contains, et c'est le seul qui a fonctionné. Merci!
protoEvangelion

1

essaye celui-là:

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.