Come verificare in Javascript se un elemento è contenuto in un altro


202

Come posso verificare se un elemento DOM è figlio di un altro elemento DOM? Ci sono metodi integrati per questo? Ad esempio, qualcosa del tipo:

if (element1.hasDescendant(element2)) 

o

if (element2.hasParent(element1)) 

Se no, allora qualche idea su come farlo? Deve anche essere cross-browser. Dovrei anche menzionare che il bambino potrebbe essere nidificato molti livelli al di sotto del genitore.



Ritengo che poiché JS è oggi uno "standard live", la risposta accettata dovrebbe essere ricontrollata per indicare il metodo "conatins" come risposta accettata.
David Taubmann,

Risposte:


219

Aggiornamento: ora esiste un modo nativo per raggiungere questo obiettivo. Node.contains(). Menzionato nel commento e anche sotto le risposte.

Vecchia risposta:

L'uso della parentNodeproprietà dovrebbe funzionare. È anche abbastanza sicuro dal punto di vista cross-browser. Se è noto che la relazione è profonda un livello, è possibile verificarla semplicemente:

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

Se il figlio può essere nidificato arbitrariamente in profondità all'interno del genitore, è possibile utilizzare una funzione simile alla seguente per verificare la relazione:

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

Grazie per la risposta, il problema è che al bambino potrebbero essere nidificati molti livelli al di sotto del genitore.
AJ.

@AJ: ho aggiornato la mia risposta per includere una soluzione che dovrebbe funzionare per nidificare il bambino arbitrariamente in profondità nel genitore.
Asaf,

228
Se qualcuno sta arrivando a questo ora, potrebbe essere possibile utilizzare Node.contains ( developer.mozilla.org/en-US/docs/DOM/Node.contains ) che è una funzione nativa nei browser moderni.
Adam Heath

2
@JohnCarrell Non c'è Node.contains(nessuna pagina di caniuse però)
gcampbell

3
@Asaph Potresti aggiornare la tua risposta per includere il nuovo Node.contains? :)

355

Dovresti usare Node.contains, dato che ora è standard e disponibile in tutti i browser.

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


Il metodo Node.hasParent (parent) non è necessario ma sarebbe Node.prototype.hasParent = function (element) {return element.contains (this);};
llange,

6
È supportato anche nei browser mobili? I documenti mdn hanno punti interrogativi per Android, Safari, IE e Opera.
settimane

1
@thetallweeks - Funziona almeno sul mio Android 7.
Sphinxxx,

5
avresti almeno potuto aggiungere un esempio
Nino Škopac,

2
Questa è una buona risposta, ma l'esempio sarebbe davvero bello:var parent = document.getElementById('menu'); var allElements = document.getElementsByTagName('a'); if (parent.contains(allElements[i]) { alert('Link is inside meni'); }
baron_bartek il

45

Ho dovuto solo condividere il "mio".

Sebbene concettualmente identica alla risposta di Asaph (che beneficia della stessa compatibilità cross-browser, anche IE6), è molto più piccola e risulta utile quando le dimensioni sono un premio e / o quando non è necessario così spesso.

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

..o come one-liner ( solo 64 caratteri !):

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

e jsfiddle qui .


Utilizzo:
childOf(child, parent) restituisce valore booleanotrue| false.

Spiegazione:
while valuta finché dura la condizione whiletrue.
L'&&operatore (AND) restituisce questo vero / falso booleano dopo aver valutato il lato sinistro e il lato destro, ma solo se il lato sinistro era true ( left-hand && right-hand) .

Il lato sinistro (di &&) è: (c=c.parentNode).
Questo assegnerà prima parentNodedi tutto ca, cquindi l'operatore AND valuterà il risultato ccome un valore booleano.
Poiché parentNoderitorna nullse non è rimasto alcun genitore e nullviene convertito in false, il ciclo while si interromperà correttamente quando non ci sono più genitori.

Il lato destro (di &&) è: c!==p.
L' !==operatore di confronto è " non esattamente uguale a". Quindi, se il genitore del bambino non è il genitore (specificato) true, ma se il genitore del bambino è il genitore, lo valuta false.
Pertanto, se restituisce c!==pfalse, l' &&operatore ritorna falsecome condizione while e il ciclo while si interrompe. (Nota che non è necessario un while-body ed ;è richiesto il punto e virgola di chiusura .)

Quindi, quando il ciclo while termina, cè o un nodo (non null) quando ha trovato un padre O lo è null(quando il ciclo ha funzionato fino alla fine senza trovare una corrispondenza).

Quindi semplicemente returnquel fatto (convertito in valore booleano, invece del nodo) con return !!c;:: l' operatore !( NOToperatore) inverte un valore booleano ( truediventa falsee viceversa).
!cconverte c(nodo o null) in un valore booleano prima che possa invertire tale valore. Così l'aggiunta di un secondo !( !!c) converte questa falsa torna a true (che è il motivo per cui una doppia !!viene spesso usata per 'convertire qualsiasi cosa per booleana').


Extra:
il corpo / payload della funzione è così piccolo che, a seconda del caso (come quando non viene usato spesso e appare solo una volta nel codice), si potrebbe persino omettere la funzione (wrapping) e usare solo il ciclo 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
}

invece di:

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: il rigoroso confronto sull'uguaglianza ( !==) può migliorare la velocità rendendo chiaro al compilatore che può saltare il controllo del tipo e le fasi di conversione implicita facoltativa che si verificherebbero in un confronto di uguaglianza allentata, migliorando così la velocità (descrivendo più accuratamente ciò che vuole accadere, il che è anche vantaggioso per il programmatore). Mi sembra anche di ricordare da quando ho scritto questo, che solo il confronto libero ( !=) era o molto soggetto a errori o non funzionava affatto in alcuni browser più vecchi (sospetto che fosse in IE6, ma ho dimenticato ).
GitaarLAB,

29

Un'altra soluzione che non è stata menzionata:

Esempio qui

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

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

Non importa se l'elemento è un figlio diretto, funzionerà a qualsiasi profondità.


In alternativa, usando il .contains()metodo :

Esempio qui

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

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

19

Dai un'occhiata a 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;
}

Altre relazioni sono DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_PRECEDINGe DOCUMENT_POSITION_FOLLOWING.

Non supportato in IE <= 8.


interessante! Questo mi ricorda una funzione JS che ho sviluppato nell'anno 2003 ... Mi ha preso alcuni giorni e ho avuto l'aiuto di alcuni sviluppatori JS ... Incredibile (e triste) che al giorno d'oggi non c'è ancora nessun trascinamento completo o implementazione completa del browser adatta agli oggetti .
David Taubmann,


2

Mi sono imbattuto in un meraviglioso codice per verificare se un elemento è figlio o meno di un altro elemento. Devo usare questo perché IE non supporta il.contains metodo element. Spero che questo possa aiutare anche gli altri.

Di seguito è la funzione:

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

Ho letteralmente provato ogni altro polyfill su questa pagina, incluso .contains, e questo è l'unico che ha funzionato. Grazie!
protoEvangelion,

1

prova questo:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.