Cómo verificar en Javascript si un elemento está contenido dentro de otro

202

¿Cómo puedo verificar si un elemento DOM es hijo de otro elemento DOM? ¿Hay algún método incorporado para esto? Por ejemplo, algo como:

if (element1.hasDescendant(element2)) 

o

if (element2.hasParent(element1)) 

Si no, ¿alguna idea de cómo hacer esto? También debe ser un navegador cruzado. También debo mencionar que el niño podría estar anidado muchos niveles por debajo del padre.

AJ
fuente
Creo que, como JS es hoy en día un "estándar en vivo", la respuesta aceptada debería volver a comprobarse para indicar el método "conatins" como la respuesta aceptada.
DavidTaubmann el

Respuestas:

219

Actualización: ahora hay una forma nativa de lograr esto. Node.contains(). Mencionado en comentarios y respuestas a continuación también.

Vieja respuesta:

Usar la parentNodepropiedad debería funcionar. También es bastante seguro desde el punto de vista del navegador cruzado. Si se sabe que la relación tiene un nivel de profundidad, puede verificarla simplemente:

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

Si el hijo puede anidarse arbitrariamente en el interior del padre, puede usar una función similar a la siguiente para probar la relación:

function isDescendant(parent, child) {
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}
Asaph
fuente
Gracias por su respuesta, el problema es que el niño podría estar anidado muchos niveles por debajo del padre.
AJ.
@AJ: Actualicé mi respuesta para incluir una solución que debería funcionar para anidar al niño de manera arbitraria en el padre.
Asaph
228
Si alguien está llegando a esto ahora, es posible que pueda usar Node.contains ( developer.mozilla.org/en-US/docs/DOM/Node.contains ), que es una función nativa en los navegadores modernos.
Adam Heath
2
@JohnCarrell Hay Node.contains(sin embargo, no hay página de caniuse)
gcampbell
3
@Asaph ¿Podría actualizar su respuesta para incluir la nueva Node.contains? :)
355

Debe usarlo Node.contains, ya que ahora es estándar y está disponible en todos los navegadores.

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

Brian Di Palma
fuente
El método Node.hasParent (parent) es innecesario pero sería Node.prototype.hasParent = function (element) {return element.contains (this);};
llange
66
¿También es compatible con navegadores móviles? Los documentos mdn tienen signos de interrogación para Android, Safari, IE y Opera.
thetallweeks
1
@thetallweeks: funciona en mi Android 7 al menos.
Sphinxxx
55
al menos podrías haber agregado un ejemplo
Nino Škopac,
2
Esta es una buena respuesta, pero el ejemplo sería realmente agradable:var parent = document.getElementById('menu'); var allElements = document.getElementsByTagName('a'); if (parent.contains(allElements[i]) { alert('Link is inside meni'); }
baron_bartek
45

Solo tenía que compartir 'el mío'.

Aunque conceptualmente es lo mismo que la respuesta de Asaph (que se beneficia de la misma compatibilidad entre navegadores, incluso IE6), es mucho más pequeño y resulta útil cuando el tamaño es muy alto y / o cuando no se necesita con tanta frecuencia.

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

..o como una línea (¡ solo 64 caracteres !):

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

y jsfiddle aquí .


Uso:
childOf(child, parent) devuelve booleanotrue| false.

Explicación: se
while evalúa siempre que la condición while se evalúe comotrue.
El&&operador (AND) devuelve este booleano verdadero / falso después de evaluar el lado izquierdo y el lado derecho, pero solo si el lado izquierdo era verdadero ( left-hand && right-hand) .

El lado izquierdo (de &&) es: (c=c.parentNode).
Esto asignará primero el parentNodede ca cy luego el operador AND evaluará el resultado ccomo un booleano.
Como parentNoderegresa nullsi no queda ningún padre y nullse convierte a false, el ciclo while se detendrá correctamente cuando no haya más padres.

El lado derecho (de &&) es: c!==p.
El !==operador de comparación ' no es exactamente igual a'. Entonces, si el padre del niño no es el padre (usted especificó) se evalúa true, pero si el padre del niño es el padre, entonces se evalúa false.
Entonces, si se c!==p evalúa como falso, el &&operador regresa falsecomo la condición while y el ciclo while se detiene. (Tenga en cuenta que no es necesario un cuerpo de tiempo y ;se requiere el punto y coma de cierre )

Entonces, cuando finaliza el ciclo while, ces un nodo (no null) cuando encuentra un padre O lo es null(cuando el ciclo se ejecuta hasta el final sin encontrar una coincidencia).

Por lo tanto, simplemente returnese hecho (convertido como valor booleano, en lugar del nodo) con return !!c;:: el !( NOToperador) invierte un valor booleano (se trueconvierte falsey viceversa).
!cconvierte c(nodo o nulo) a un valor booleano antes de que pueda invertir ese valor. Entonces, agregar un segundo !( !!c) convierte este falso de nuevo en verdadero (razón por la cual un doble!! menudo se usa para 'convertir cualquier cosa a booleano').


Extra:
El cuerpo / carga útil de la función es tan pequeña que, dependiendo del caso (como cuando no se usa con frecuencia y aparece solo una vez en el código), uno podría incluso se omitir la función (ajuste) y solo usar el 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
}

en vez 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
}
GitaarLAB
fuente
44
@SolomonUcko: La estricta comparación de igualdad ( !==) puede mejorar la velocidad al dejar en claro al compilador que puede omitir la verificación de tipo y los pasos opcionales de conversión implícita que ocurrirían en una comparación de igualdad suelta, mejorando así la velocidad (al describir con mayor precisión lo que querer suceder, lo que también es beneficioso para el programador). También recuerdo cuando escribí esto, que solo la comparación ( !=) era muy propensa a errores o no funcionaba en algunos navegadores más antiguos (sospecho que estaba en IE6, pero he olvidado )
GitaarLAB
29

Otra solución que no se mencionó:

Ejemplo aquí

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

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

No importa si el elemento es un hijo directo, funcionará a cualquier profundidad.


Alternativamente, usando el .contains()método :

Ejemplo aquí

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

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

Eche un vistazo al Nodo # 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;
}

Otras relaciones incluyen DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_PRECEDINGy DOCUMENT_POSITION_FOLLOWING.

No es compatible con IE <= 8.


fuente
¡interesante! Esto me recuerda a una función JS que desarrollé en el año 2003 ... Me tomó algunos días y obtuve ayuda de algunos desarrolladores de JS ... Increíble (y triste) que hoy en día todavía no hay una implementación completa de navegador de arrastrar y soltar ni ajuste completo de objetos .
DavidTaubmann el
17

Puedes usar el método contiene

var result = parent.contains(child);

o puedes intentar usar compareDocumentPosition ()

var result = nodeA.compareDocumentPosition(nodeB);

El último es más poderoso: devuelve una máscara de bits como resultado.

Iegor Kozakov
fuente
2

Me encontré con un maravilloso código para verificar si un elemento es hijo de otro elemento. Tengo que usar esto porque IE no admite el .containsmétodo del elemento. Espero que esto ayude a otros también.

A continuación se muestra la función:

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
fuente
Literalmente probé cualquier otro polyfill en esta página, incluidos .contains, y este es el único que funcionó. ¡Gracias!
protoEvangelion
1

prueba este:

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";
}
usuario3675072
fuente
-2

Recientemente me encontré con esta función que podría hacer:

Node.compareDocumentPosition ()

ZioCain
fuente
2
Voto negativo porque la misma respuesta ya existía varias veces con más información que solo un enlace externo.
Stefan Fabian