Eliminar todos los elementos secundarios de un nodo DOM en JavaScript


876

¿Cómo haría para eliminar todos los elementos secundarios de un nodo DOM en JavaScript?

Digamos que tengo el siguiente (feo) HTML:

<p id="foo">
    <span>hello</span>
    <div>world</div>
</p>

Y agarro el nodo que quiero así:

var myNode = document.getElementById("foo");

¿Cómo podría eliminar a los niños de foomodo que solo <p id="foo"></p>quede?

¿Podría simplemente hacer:

myNode.childNodes = new Array();

o debería usar alguna combinación de removeElement?

Me gustaría que la respuesta fuera directamente DOM; aunque puntos adicionales si también proporciona una respuesta en jQuery junto con la respuesta solo DOM.

Respuestas:


1675

Opción 1 A: compensación innerHTML.

  • Este enfoque es simple, pero puede no ser adecuado para aplicaciones de alto rendimiento porque invoca el analizador HTML del navegador (aunque los navegadores pueden optimizar para el caso donde el valor es una cadena vacía).

doFoo.onclick = () => {
  const myNode = document.getElementById("foo");
  myNode.innerHTML = '';
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <span>Hello</span>
</div>
<button id='doFoo'>Remove via innerHTML</button>

Opción 1 B: compensación textContent

  • Como arriba, pero uso .textContent. Según MDN, esto será más rápido que los innerHTMLnavegadores que no invocarán sus analizadores HTML y en su lugar reemplazarán inmediatamente a todos los elementos secundarios del elemento con un solo #textnodo.

doFoo.onclick = () => {
  const myNode = document.getElementById("foo");
  myNode.textContent = '';
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <span>Hello</span>
</div>
<button id='doFoo'>Remove via textContent</button>

Opción 2 A: bucle para eliminar cada lastChild:

  • Se utilizó una edición anterior de esta respuesta firstChild, pero se actualiza para usarla lastChildcomo en informática, en general , es significativamente más rápido eliminar el último elemento de una colección que eliminar el primer elemento (dependiendo de cómo se implemente la colección )
  • El bucle continúa comprobando firstChild solo en caso de que sea más rápido verificar firstChildque lastChild(p. Ej., Si la UA implementa la lista de elementos como una lista enlazada dirigida).

doFoo.onclick = () => {
  const myNode = document.getElementById("foo");
  while (myNode.firstChild) {
    myNode.removeChild(myNode.lastChild);
  }
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <span>Hello</span>
</div>
<button id='doFoo'>Remove via lastChild-loop</button>

Opción 2 B: bucle para eliminar cada lastElementChild:

  • Este enfoque conserva todos los elementos secundarios Element(es decir, #textnodos y no <!-- comments -->) de los padres (pero no sus descendientes), y esto puede ser deseable en su aplicación (por ejemplo, algunos sistemas de plantillas que utilizan comentarios HTML en línea para almacenar instrucciones de plantilla).
  • Este enfoque no se usó hasta los últimos años, ya que Internet Explorer solo agregó soporte para lastElementChildIE9.

doFoo.onclick = () => {
  const myNode = document.getElementById("foo");
  while (myNode.lastElementChild) {
    myNode.removeChild(myNode.lastElementChild);
  }
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <!-- This comment won't be removed -->
  <span>Hello <!-- This comment WILL be removed --></span>
  <!-- But this one won't. -->
</div>
<button id='doFoo'>Remove via lastElementChild-loop</button>

Bonus: Element.clearChildrenparche de mono:

  • Podemos agregar una nueva propiedad de método al Elementprototipo en JavaScript para simplificar la invocación a solo el.clearChildren()(donde elestá cualquier objeto de elemento HTML).
  • (Hablando estrictamente, esto es un parche de mono, no un polyfill, ya que esta no es una característica DOM estándar o una característica que falta. Tenga en cuenta que el parche de mono se desaconseja legítimamente en muchas situaciones).

if( typeof Element.prototype.clearChildren === 'undefined' ) {
    Object.defineProperty(Element.prototype, 'clearChildren', {
      configurable: true,
      enumerable: false,
      value: function() {
        while(this.firstChild) this.removeChild(this.lastChild);
      }
    });
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <span>Hello <!-- This comment WILL be removed --></span>
</div>
<button onclick="this.previousElementSibling.clearChildren()">Remove via monkey-patch</button>


66
Desde jQuery, se pueden considerar estos dos problemas: este método elimina no solo elementos secundarios (y otros descendientes), sino también cualquier texto dentro del conjunto de elementos coincidentes. Esto se debe a que, de acuerdo con la especificación DOM, cualquier cadena de texto dentro de un elemento se considera un nodo hijo de ese elemento. Y "Para evitar pérdidas de memoria, jQuery elimina otras construcciones, como los manejadores de datos y eventos, de los elementos secundarios antes de eliminar los elementos mismos".
jottos

104
Por cierto, usar lastChild parece ser un poco más efectivo jsperf.com/innerhtml-vs-removechild/15
Andrey Lushnikov

24
innerHTML solo funciona si solo se trata de HTML. Si hay, por ejemplo, SVG dentro, solo funcionará la eliminación de elementos
stwissel

34
NUNCA NUNCA NUNCA use innerHTML = ''. No lo hagas El problema es que los elementos parecen eliminados, pero internamente, los nodos persisten y ralentizan las cosas. Tienes que eliminar todos los nodos individuales por una razón. Probado tanto en Google Chrome como en IE. Considere eliminar la "solución" innerHTML ya que está mal.
Kenji

15
@vsync No creo que el comentario de Kenji esté bien fundado. Hacerlo innerHTML = ''es más lento, pero no creo que esté "mal". Cualquier reclamo de pérdida de memoria debe ser respaldado con evidencia.
Chris Middleton

115

La respuesta actualmente aceptada es incorrecta sobre innerHTMLser más lento (al menos en IE y Chrome), como se mencionó correctamente en m93a.

Chrome y FF son mucho más rápidos con este método (que destruirá los datos jquery adjuntos):

var cNode = node.cloneNode(false);
node.parentNode.replaceChild(cNode, node);

en un distante segundo lugar para FF y Chrome, y más rápido en IE:

node.innerHTML = '';

InnerHTML no destruirá sus controladores de eventos ni romperá las referencias de jquery , también se recomienda como solución aquí: https://developer.mozilla.org/en-US/docs/Web/API/Element.innerHTML .

El método de manipulación DOM más rápido (aún más lento que los dos anteriores) es la eliminación de Rango, pero los rangos no son compatibles hasta IE9.

var range = document.createRange();
range.selectNodeContents(node);
range.deleteContents();

Los otros métodos mencionados parecen ser comparables, pero mucho más lentos que innerHTML, excepto el atípico, jquery (1.1.1 y 3.1.1), que es considerablemente más lento que cualquier otra cosa:

$(node).empty();

Evidencia aquí:

http://jsperf.com/innerhtml-vs-removechild/167 http://jsperf.com/innerhtml-vs-removechild/300 https://jsperf.com/remove-all-child-elements-of-a- dom-node-in-javascript (Nueva url para el reinicio de jsperf porque la edición de la antigua url no funciona)

El "bucle de prueba" de Jsperf a menudo se entiende como "por iteración", y solo la primera iteración tiene nodos para eliminar, por lo que los resultados no tienen sentido, al momento de la publicación había pruebas en este hilo configuradas incorrectamente.


2
Tu jsperf está completamente roto. Eso no es muy buena evidencia.
bryc

44
Esta es actualmente la mejor respuesta. Todo lo demás en este hilo es desinformación (!) Gracias por limpiar todas esas viejas pruebas malas.
Ben

66
"InnerHTML no destruirá sus controladores de eventos ni desordenará ninguna referencia jquery" Se quedará huérfano de los datos almacenados jQuery.cache, causando una pérdida de memoria porque la única referencia disponible para administrar esos datos estaba en los elementos que acaba de destruir.

1
"InnerHTML no destruirá sus controladores de eventos ni desordenará ninguna referencia de jquery" se refiere al contenedor, no a los elementos secundarios. cloneNodeapaga el contenedor, lo que significa que las referencias al contenedor no se mantienen (jquery o no). La memoria caché de Jquery puede hacer que valga la pena usar jquery para eliminar elementos si tiene datos jquery adjuntos. Esperemos que cambien a WeakMaps en algún momento. La limpieza (o incluso la existencia de) $ .cache no parece estar documentada oficialmente.
npjohns

10
Las pruebas jsperf están rotas aquí. El motor de prueba informa lo siguiente: "Error: nodos Dom no recreados durante las iteraciones, los resultados de la prueba no tendrán sentido".
senderle

73

Use Javascript moderno, con remove!

const parent = document.getElementById("foo");
while (parent.firstChild) {
    parent.firstChild.remove();
}

Esta es una nueva forma de escribir la eliminación de nodos en ES5. Es vanilla JS y se lee mucho mejor que las versiones anteriores.

La mayoría de los usuarios deben tener navegadores modernos o puede transpilar si es necesario.

Soporte del navegador : 95% de diciembre de 2019


14
El bucle for no funcionará ya que parent.children / parent.childNodes son listas activas; llamar a remove modifica la lista y, por lo tanto, no se garantiza que su iterador repita todo. En Chrome, por ejemplo, terminas saltando todos los demás nodos. Es mejor usar un while (parent.firstChild) { parent.removeChild(parent.firstChild); }bucle. developer.mozilla.org/en-US/docs/Web/API/ParentNode/children developer.mozilla.org/en-US/docs/Web/API/Node/childNodes
qix

3
Taquigrafía genial, aunque menos legible:while (parent.firstChild && !parent.firstChild.remove());
Gibolt

1
Una [...parent.childNodes].forEach(el => el.remove());

1
Esto no funciona en mecanografiado ... en su lugar usewhile (selectElem.firstElementChild) { selectElem.firstElementChild.remove(); }
John Henckel

43
var myNode = document.getElementById("foo");
var fc = myNode.firstChild;

while( fc ) {
    myNode.removeChild( fc );
    fc = myNode.firstChild;
}

Si hay alguna posibilidad de que jQuery haya afectado a los descendientes, debe usar algún método que limpie los datos de jQuery.

$('#foo').empty();

El .empty()método jQuery asegurará que todos los datos que jQuery asocie con los elementos que se eliminen se limpiarán.

Si simplemente usa DOMmétodos para eliminar a los hijos, esos datos permanecerán.


El método innerHTML es, con mucho, el más eficaz. Dicho esto, los datos que limpia jquery's .empty (): Elimina los datos () dentro de jquery asociados con las etiquetas secundarias usando un bucle recursivo (lento, pero puede reducir el uso de memoria significativamente), evita fugas de memoria SI conecta eventos sin jquery, como usar .onclick = .... Si no tiene tales eventos en los niños que se eliminarán, la configuración de innerHTML no se filtrará. Entonces la mejor respuesta es: depende.
Chris Moschini

1
¿Por qué no simplemente poner la expresión firstChild directamente en la condición del bucle while? while ( myNode.firstChild ) {
Victor Zamanian

while(fc = myNode.firstChild){ myNode.removeChild(fc); }
Valen

36

Si usa jQuery:

$('#foo').empty();

Si no lo haces:

var foo = document.getElementById('foo');
while (foo.firstChild) foo.removeChild(foo.firstChild);

20

El más rápido...

var removeChilds = function (node) {
    var last;
    while (last = node.lastChild) node.removeChild(last);
};

Gracias a Andrey Lushnikov por su enlace a jsperf.com (¡sitio genial!).

EDITAR: para ser claros, no hay diferencia de rendimiento en Chrome entre firstChild y lastChild. La respuesta principal muestra una buena solución para el rendimiento.


Lo mismo sin advertencia de LINT: clear: function (container) {for (;;) {var child = container.lastChild; si (! niño) se rompe; container.removeChild (hijo); }}
cskwg

9

Si solo desea tener el nodo sin sus hijos, también puede hacer una copia de esta manera:

var dupNode = document.getElementById("foo").cloneNode(false);

Depende de lo que intentes lograr.


3
¿Tal vez podrías seguir esto con parent.replaceChild(cloned, original)? Eso podría ser más rápido que eliminar a los niños uno por uno y debería funcionar en todo lo que sea compatible con el DOM (por lo tanto, cada tipo de documento XML, incluido SVG). Configurar innerHTML también puede ser más rápido que eliminar a los niños uno por uno, pero eso no funciona en nada más que en documentos HTML. Debería probar eso alguna vez.
Maarten

13
Eso no copiará los oyentes de eventos agregados usando addEventListener.
Mateo

Si puede vivir con esa limitación, sin duda es rápido: jsperf.com/innerhtml-vs-removechild/137
DanMan

7

Aquí hay otro enfoque:

function removeAllChildren(theParent){

    // Create the Range object
    var rangeObj = new Range();

    // Select all of theParent's children
    rangeObj.selectNodeContents(theParent);

    // Delete everything that is selected
    rangeObj.deleteContents();
}

1
Esto es interesante pero parece bastante lento en comparación con otros métodos: jsperf.com/innerhtml-vs-removechild/256
Polaris878

6
element.textContent = '';

Es como innerText, excepto estándar. Es un poco más lento que removeChild(), pero es más fácil de usar y no hará una gran diferencia en el rendimiento si no tiene demasiadas cosas para eliminar.


Un nodo de texto no es un elemento
check_ca

lo siento, tienes razón, elimina de hecho todos los elementos
secundarios

Esto no funcionará en versiones anteriores de Internet Explorer.
pwdst

4

En respuesta a DanMan, Maarten y Matt. Clonar un nodo para configurar el texto es, de hecho, una forma viable en mis resultados.

// @param {node} node
// @return {node} empty node
function removeAllChildrenFromNode (node) {
  var shell;
  // do not copy the contents
  shell = node.cloneNode(false);

  if (node.parentNode) {
    node.parentNode.replaceChild(shell, node);
  }

  return shell;
}

// use as such
var myNode = document.getElementById('foo');
myNode = removeAllChildrenFromNode( myNode );

Además, esto funciona para los nodos que no están en el dom que devuelven nulo al intentar acceder al parentNode. Además, si necesita estar seguro, un nodo está vacío antes de agregar contenido, esto es realmente útil. Considere el caso de uso debajo.

// @param {node} node
// @param {string|html} content
// @return {node} node with content only
function refreshContent (node, content) {
  var shell;
  // do not copy the contents
  shell = node.cloneNode(false);

  // use innerHTML or you preffered method
  // depending on what you need
  shell.innerHTML( content );

  if (node.parentNode) {
    node.parentNode.replaceChild(shell, node);
  }

  return shell;
}

// use as such
var myNode = document.getElementById('foo');
myNode = refreshContent( myNode );

Considero que este método es muy útil al reemplazar una cadena dentro de un elemento, si no está seguro de lo que contendrá el nodo, en lugar de preocuparse por cómo limpiar el desorden, comience de nuevo.


3
Lo peor de todo. Si está reemplazando el nodo original con su clon, está perdiendo la referencia al nodo original. ninguno de los controladores de eventos y otros enlaces no funcionarán.
Arun Aravind

4

Esto es lo que suelo hacer:

HTMLElement.prototype.empty = function() {
    while (this.firstChild) {
        this.removeChild(this.firstChild);
    }
}

Y listo, más tarde puedes vaciar cualquier elemento dom con:

anyDom.empty()

¡Me gusta esta solución! ¿Alguna razón por la que debería usar esto sobre la configuración de innerHTML en una cadena en blanco?
TheCodenator

rendimiento: domEl.innerHTML = ""es pobre y se considera una mala práctica
Ado Ren

3

Hay un par de opciones para lograr eso:

El más rápido ():

while (node.lastChild) {
  node.removeChild(node.lastChild);
}

Alternativas (más lentas):

while (node.firstChild) {
  node.removeChild(node.firstChild);
}

while (node.hasChildNodes()) {
  node.removeChild(node.lastChild);
}

Benchmark con las opciones sugeridas


3
var empty_element = function (element) {

    var node = element;

    while (element.hasChildNodes()) {              // selected elem has children

        if (node.hasChildNodes()) {                // current node has children
            node = node.lastChild;                 // set current node to child
        }
        else {                                     // last child found
            console.log(node.nodeName);
            node = node.parentNode;                // set node to parent
            node.removeChild(node.lastChild);      // remove last node
        }
    }
}

Esto eliminará todos los nodos dentro del elemento.


... porque es un complejo innecesario y no se explica cómo funciona (lo que en realidad no es obvio), sospecho.
Periata Breatta

2

innerText es el ganador! http://jsperf.com/innerhtml-vs-removechild/133 . En todas las pruebas anteriores, el dom interno del nodo primario se eliminó en la primera iteración y luego innerHTML o removeChild se aplicaron al div vacío.


55
innerTextSin embargo, es una cosa propietaria de MS. Solo digo.
DanMan 01 de

1
Estoy bastante seguro de que esta es una prueba defectuosa: se basa en boxestar disponible no como una variable sino a través de la búsqueda DOM basada en la identificación, y dado que el whilebucle hace esta llamada lenta muchas veces, se penaliza.
nrabinowitz

2

La forma más sencilla de eliminar los nodos secundarios de un nodo a través de Javascript

var myNode = document.getElementById("foo");
while(myNode.hasChildNodes())
{
   myNode.removeChild(myNode.lastChild);
}

2

vi gente haciendo:

while (el.firstNode) {
    el.removeChild(el.firstNode);
}

entonces alguien dijo usando el.lastNode es más rápido

Sin embargo, creo que este es el más rápido:

var children = el.childNodes;
for (var i=children.length - 1; i>-1; i--) {
    el.removeNode(children[i]);
}

¿Qué piensas?

PD: este tema fue un salvavidas para mí. mi complemento de Firefox fue rechazado porque utilicé innerHTML. Ha sido un hábito durante mucho tiempo. entonces lo entiendo. y de hecho noté una diferencia de velocidad. al cargar el innerhtml tardó un tiempo en actualizarse, sin embargo, al agregar addElement es instantáneo.


1
bueno, creo que si es más rápido o no, algunas pruebas jsperf pueden probar cualquier caso
Nikos M.

super trabajo gracias por esas pruebas. aunque no incluyen el for (var i=0...uno. Pero esto es lo que obtuve cuando ejecuté esas pruebas: i.imgur.com/Mn61AmI.png, así que en esas tres pruebas firstNode fue más rápido que lastNode e innerHTML, lo cual es realmente genial
Noitidart

agregué las pruebas: jsperf.com/innerhtml-vs-removechild/220 cosas interesantes haciendo el método el.firstNode es la forma más rápida que parece
Noitidart

2

Usar un bucle de rango me parece lo más natural:

for (var child of node.childNodes) {
    child.remove();
}

Según mis mediciones en Chrome y Firefox, es aproximadamente 1.3 veces más lento. En circunstancias normales, esto quizás no importe.


2
 let el = document.querySelector('#el');
 if (el.hasChildNodes()) {
      el.childNodes.forEach(child => el.removeChild(child));
 }

1
Por favor, explique en respuesta ¿qué está haciendo su código?
Nirav Joshi el

1
Se explica por sí mismo
blacksheep

1

En general, JavaScript utiliza matrices para hacer referencia a listas de nodos DOM. Entonces, esto funcionará bien si tiene interés en hacerlo a través de la matriz HTMLElements. Además, vale la pena señalar, porque estoy usando una referencia de matriz en lugar de protocolos de JavaScript, esto debería funcionar en cualquier navegador, incluido IE.

while(nodeArray.length !== 0) {
  nodeArray[0].parentNode.removeChild(nodeArray[0]);
}

1

¿Por qué no estamos siguiendo el método más simple aquí "eliminar" en el interior mientras?

const foo = document.querySelector(".foo");
while (foo.firstChild) {
  foo.firstChild.remove();     
}
  • Seleccionar el div padre
  • Usando el método "remove" dentro de un bucle While para eliminar el primer elemento hijo, hasta que no quede ninguno.

Explique sus líneas de código para que otros usuarios puedan entender su funcionalidad. ¡Gracias!
Ignacio Ara

@IgnacioAra Hecho!
Neelansh Verma

1

Acabo de ver a alguien mencionar esta pregunta en otra y pensé que agregaría un método que aún no vi:

function clear(el) {
    el.parentNode.replaceChild(el.cloneNode(false), el);
}

var myNode = document.getElementById("foo");
clear(myNode);

La función clear es tomar el elemento y usar el nodo padre para reemplazarse con una copia sin sus hijos. No se gana mucho rendimiento si el elemento es escaso, pero cuando el elemento tiene un grupo de nodos, se obtienen ganancias de rendimiento.


1
En realidad, parece que se mencionó aquí stackoverflow.com/a/15441725/576767
Félix Gagnon-Grenier el

Felix Debo haberme perdido eso, gracias por la corrección +1.
Harry Chilinguerian el

1

Enfoque funcional único:

const domChildren = (el) => Array.from(el.childNodes)
const domRemove = (el) => el.parentNode.removeChild(el)
const domEmpty = (el) => domChildren(el).map(domRemove)

"childNodes" en domChildren dará una lista de nodos de los elementos secundarios inmediatos, que está vacía cuando no se encuentra ninguno. Para mapear sobre la lista de nodos, domChildren la convierte en matriz. domEmpty asigna una función domRemove sobre todos los elementos que la eliminan.

Ejemplo de uso:

domEmpty(document.body)

elimina a todos los niños del elemento del cuerpo.


0

Otras formas en jQuery

var foo = $("#foo");
foo.children().remove();
//or
$("*", foo ).remove();
//or
foo.html("");
//or
foo.empty();

Votación negativa porque OP dijo que las respuestas en DOM directo son preferidas.
bwindels 01 de

Además, jQuery tiene .empty()método, solo $("#foo").empty()sería suficiente
YakovL

-1

simplemente solo IE:

parentElement.removeNode(true);

true - significa hacer una extracción profunda - lo que significa que también se eliminan todos los niños


1
¿ES DECIR? Este es el siglo 21!
everlasto

-1

La forma más fácil:

let container = document.getElementById("containerId");
container.innerHTML = "";

Mientras esto funciona, tenga cuidado de que container.innerHTML = nullInternet Explorer muestre el texto null. Entonces, esto realmente no elimina a los niños, diría.
Arjan

-1

simple y rápido usando for loop !!

var myNode = document.getElementById("foo");

    for(var i = myNode.childNodes.length - 1; i >= 0; --i) {
      myNode.removeChild(myNode.childNodes[i]);
    }

¡Esto no funcionará en la <span>etiqueta!


en la solución sobre todo, ningún código está eliminando la <span>etiqueta
Mohit Karkar

-3

Si quieres volver a poner algo en eso div, innerHTMLprobablemente sea mejor.

Mi ejemplo:

<ul><div id="result"></div></ul>

<script>
  function displayHTML(result){
    var movieLink = document.createElement("li");
    var t = document.createTextNode(result.Title);
    movieLink.appendChild(t);
    outputDiv.appendChild(movieLink);
  }
</script>

Si uso el método .firstChildo .lastChildla displayHTML()función no funciona después, pero no hay problema con el .innerHTMLmétodo.


-4

Este es un javascript puro, no estoy usando jQuery, pero funciona en todos los navegadores, incluso en IE, y es muy fácil de entender

   <div id="my_div">
    <p>Paragraph one</p>
    <p>Paragraph two</p>
    <p>Paragraph three</p>
   </div>
   <button id ="my_button>Remove nodes ?</button>

   document.getElementById("my_button").addEventListener("click",function(){

  let parent_node =document.getElemetById("my_div"); //Div which contains paagraphs

  //Let find numbers of child inside the div then remove all
  for(var i =0; i < parent_node.childNodes.length; i++) {
     //To avoid a problem which may happen if there is no childNodes[i] 
     try{
       if(parent_node.childNodes[i]){
         parent_node.removeChild(parent_node.childNodes[i]);
       }
     }catch(e){
     }
  }

})

or you may simpli do this which is a quick way to do

document.getElementById("my_button").addEventListener("click",function(){

 let parent_node =document.getElemetById("my_div");
 parent_node.innerHTML ="";

})

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.