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.



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

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


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
}

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
}

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.


¡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


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

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.