Remova todos os elementos filhos de um nó DOM em JavaScript


873

Como eu removeria todos os elementos filhos de um nó DOM em JavaScript?

Digamos que eu tenha o seguinte (feio) HTML:

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

E eu pego o nó que eu quero assim:

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

Como eu poderia remover os filhos de foomodo a <p id="foo"></p>restar apenas ?

Eu poderia apenas fazer:

myNode.childNodes = new Array();

ou devo usar alguma combinação de removeElement?

Eu gostaria que a resposta fosse diretamente DOM; embora pontos extras se você também fornecer uma resposta no jQuery junto com a resposta somente do DOM.

Respostas:


1674

Opção 1 A: Compensação innerHTML.

  • Essa abordagem é simples, mas pode não ser adequada para aplicativos de alto desempenho, pois invoca o analisador HTML do navegador (embora os navegadores possam otimizar para o caso em que o valor é uma sequência vazia).

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>

Opção 1 B: Compensação textContent

  • Como acima, mas use .textContent. De acordo com o MDN, isso será mais rápido do innerHTMLque os navegadores não chamarão seus analisadores HTML e, em vez disso, substituirão imediatamente todos os filhos do elemento por um único #textnó.

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>

Opção 2 A: loop para remover todos lastChild:

  • Uma edição anterior dessa resposta foi usada firstChild, mas é atualizada para uso lastChildcomo na ciência da computação, em geral , é significativamente mais rápido remover o último elemento de uma coleção do que remover o primeiro elemento (dependendo de como a coleção é implementada )
  • O loop continua a verificar firstChild apenas no caso é mais rápido para verificar firstChilddo que lastChild(por exemplo, se a lista elemento é implementado como uma lista ligada dirigida pela UA).

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>

Opção 2 B: loop para remover todos os itens lastElementChild:

  • Essa abordagem preserva todos os não Element(principalmente #textnós e <!-- comments -->) filhos dos pais (mas não seus descendentes) - e isso pode ser desejável em seu aplicativo (por exemplo, alguns sistemas de modelos que usam comentários HTML embutidos para armazenar instruções de modelo).
  • Essa abordagem não foi usada até os últimos anos, pois o Internet Explorer adicionou suporte apenas para o 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>

Bônus: Element.clearChildrenpatch de macaco:

  • Podemos adicionar uma nova propriedade de método ao Elementprotótipo em JavaScript para simplificar a chamada el.clearChildren()( apenas onde elestá qualquer objeto de elemento HTML).
  • (A rigor, trata-se de um patch para macacos, não de um polyfill, pois esse não é um recurso DOM padrão ou um recurso ausente. Observe que o patch para macacos é desencorajado por várias situações).

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>


6
No jQuery, esses dois problemas podem ser considerados: Esse método remove não apenas os elementos filho (e outros descendentes), mas também qualquer texto dentro do conjunto de elementos correspondentes. Isso ocorre porque, de acordo com a especificação do DOM, qualquer sequência de texto em um elemento é considerada um nó filho desse elemento. AND "Para evitar vazamentos de memória, o jQuery remove outras construções, como manipuladores de dados e eventos dos elementos filhos, antes de remover os próprios elementos".
jottos

104
Btw, usando lastChild parece ser um pouco mais eficaz jsperf.com/innerhtml-vs-removechild/15
Andrey Lushnikov

24
innerHTML só funciona se você estiver lidando apenas com HTML. Se houver, por exemplo SVG dentro única remoção Elemento vai funcionar
stwissel

34
NUNCA NUNCA use innerHTML = ''. Não. O problema é que os itens parecem removidos, mas internamente os nós persistem e tornam as coisas mais lentas. Você precisa remover todos os nós individuais por um motivo. Testado no Google Chrome e no IE. Por favor, considere remover a "solução" innerHTML, pois está errada.
21413 Kenji

15
@ vsync Não acredito que o comentário de Kenji seja bem fundamentado. Fazer innerHTML = ''é mais lento, mas não acho que seja "errado". Quaisquer reivindicações de vazamento de memória devem ser apoiadas com evidências.
Chris Middleton

115

A resposta atualmente aceita está errada em innerHTMLser mais lenta (pelo menos no IE e no Chrome), como m93a mencionado corretamente.

Chrome e FF são muito mais rápidos usando este método (que destruirá os dados do jquery anexado):

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

em um segundo distante para FF e Chrome e mais rápido no IE:

node.innerHTML = '';

O InnerHTML não destruirá seus manipuladores de eventos nem quebrará as referências a jquery , também é recomendado como uma solução aqui: https://developer.mozilla.org/en-US/docs/Web/API/Element.innerHTML .

O método de manipulação do DOM mais rápido (ainda mais lento que os dois anteriores) é a remoção do intervalo, mas os intervalos não são suportados até o IE9.

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

Os outros métodos mencionados parecem ser comparáveis, mas muito mais lentos que o innerHTML, exceto o jquery (1.1.1 e 3.1.1), que é consideravelmente mais lento que qualquer outra coisa:

$(node).empty();

Evidência aqui:

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 (Novo URL para reinicialização do jsperf porque a edição do URL antigo não está funcionando)

O "loop por teste" do Jsperf geralmente é entendido como "por iteração", e somente a primeira iteração tem nós para remover, de modo que os resultados não fazem sentido; no momento da postagem, havia testes nesse segmento configurados incorretamente.


2
Seu jsperf está completamente quebrado. Essa não é uma evidência muito boa.
Br

4
Atualmente, esta é a melhor resposta. Tudo o mais neste segmento é desinformação (!) Obrigado por limpar todos os testes antigos e ruins.
Ben

6
"O InnerHTML não destruirá seus manipuladores de eventos nem atrapalhará nenhuma referência de jquery". Os dados órfãos serão mantidos jQuery.cache, causando um vazamento de memória, porque a única referência disponível para gerenciar esses dados estava nos elementos que você acabou de destruir.

1
"O InnerHTML não destrói seus manipuladores de eventos nem atrapalha nenhuma referência a jquery" refere-se ao contêiner, não aos filhos. cloneNodealterna o contêiner, o que significa que as referências ao contêiner não são mantidas (jquery ou de outra forma). O cache do Jquery pode valer a pena usar o jquery para remover elementos se você tiver dados do jquery anexados a eles. Espero que eles mudem para o WeakMaps em algum momento. A limpeza (ou mesmo a existência) do $ .cache não parece estar oficialmente documentada.
Npjohns

10
Os testes jsperf são quebrados aqui. O mecanismo de teste relata o seguinte: "Erro: nós do Dom não recriados durante as iterações, os resultados do teste não terão sentido".
Senderle

73

Use Javascript moderno, com remove!

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

Essa é uma maneira mais nova de gravar a remoção de nós no ES5. É JS baunilha e lê muito melhor do que as versões anteriores.

A maioria dos usuários deve ter navegadores modernos ou você pode transpilar para baixo, se necessário.

Suporte do navegador - 95% dez 2019


14
O loop for não funcionará, pois parent.children / parent.childNodes são listas ativas; chamando remove modifica a lista e, portanto, seu iterador não garante a iteração de tudo. No chrome, por exemplo, você acaba pulando todos os outros nós. Melhor usar um while (parent.firstChild) { parent.removeChild(parent.firstChild); }loop. developer.mozilla.org/en-US/docs/Web/API/ParentNode/children developer.mozilla.org/en-US/docs/Web/API/Node/childNodes
qix

3
Taquigrafia legal, embora menos legível:while (parent.firstChild && !parent.firstChild.remove());
Gibolt

1
Um one-liner para quando o desempenho não é um grande negócio:[...parent.childNodes].forEach(el => el.remove());

1
Isso não funciona à máquina ... em vez usarwhile (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;
}

Se há alguma chance de que você tem jQuery afetados descendentes, então você deve usar algum método que irá limpar os dados jQuery.

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

O .empty()método jQuery garantirá que todos os dados associados ao jQuery aos elementos removidos sejam limpos.

Se você simplesmente usar DOMmétodos para remover os filhos, esses dados permanecerão.


O método innerHTML é de longe o mais eficiente. Dito isso, os dados que estão sendo limpos pelo jquery .empty (): Elimina os dados () dentro do jquery associado às tags filho usando um loop recursivo (lento, mas pode reduzir significativamente o uso de memória), evita vazamentos de memória se você anexar eventos sem jquery, como usar .onclick = .... Se você não tiver esses eventos nos filhos a serem removidos, a configuração innerHTML não vazará. Portanto, a melhor resposta é - depende.
Chris Moschini

1
Por que não colocar a expressão firstChild diretamente na condição do loop while? while ( myNode.firstChild ) {
Victor Zamanian

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

36

Se você usa jQuery:

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

Se não o fizer:

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

20

O mais rápido...

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

Obrigado a Andrey Lushnikov pelo link para jsperf.com (site legal!).

EDIT: para ficar claro, não há diferença de desempenho no Chrome entre firstChild e lastChild. A resposta principal mostra uma boa solução para o desempenho.


A mesma coisa sem aviso LINT: clear: function (container) {for (;;) {var child = container.lastChild; se (! criança) quebrar; container.removeChild (filho); }}
cskwg

9

Se você quiser apenas ter o nó sem seus filhos, também poderá fazer uma cópia dele assim:

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

Depende do que você está tentando alcançar.


3
Talvez você possa até seguir isso com parent.replaceChild(cloned, original)? Isso pode ser mais rápido do que remover filhos um por um e deve funcionar em tudo que suporta o DOM (portanto, todos os tipos de documentos XML, incluindo SVG). Definir innerHTML também pode ser mais rápido do que remover filhos um por um, mas isso não funciona em nada além de documentos HTML. Deve testar isso há algum tempo.
Maarten

13
Isso não copiará ouvintes de eventos adicionados usando addEventListener.
Matthew

Se você pode viver com essa limitação, é certamente rápida: jsperf.com/innerhtml-vs-removechild/137
DanMan

7

Aqui está outra abordagem:

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
Isso é interessante, mas parece bastante lento em comparação com outros métodos: jsperf.com/innerhtml-vs-removechild/256
Polaris878

6
element.textContent = '';

É como o innerText, exceto o padrão. É um pouco mais lento que removeChild(), mas é mais fácil de usar e não fará muita diferença de desempenho se você não tiver muitas coisas para excluir.


Um nó de texto não é um elemento
check_ca

desculpe você está certo, ele remove na verdade todos os elementos filhos
check_ca

Isso não funcionará em versões mais antigas do Internet Explorer.
Pwdst

4

Em resposta a DanMan, Maarten e Matt. Clonar um nó, para definir o texto, é realmente uma maneira viável nos meus 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 );

Isso também funciona para nós que não estão no dom e retornam nulos ao tentar acessar o parentNode. Além disso, se você precisa estar seguro, um nó está vazio antes de adicionar conteúdo, isso é realmente útil. Considere o caso de uso abaixo.

// @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 );

Acho esse método muito útil ao substituir uma string dentro de um elemento, se você não tiver certeza do que o nó conterá, em vez de se preocupar em limpar a bagunça, comece do zero.


3
a pior coisa do mundo. se você estiver substituindo o nó original por seu clone, estará perdendo a referência ao nó original. nenhum dos manipuladores de eventos e outras ligações não funcionará.
Arun Aravind

4

Aqui está o que eu costumo fazer:

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

E pronto, mais tarde você pode esvaziar qualquer elemento dom com:

anyDom.empty()

Eu gosto desta solução! Qualquer motivo que eu deveria usar isso ao definir innerHTML para uma seqüência de caracteres em branco?
TheCodenator 26/10/19

desempenho: domEl.innerHTML = ""é ruim e considerado uma má prática
Ado Ren

3

Existem algumas opções para conseguir isso:

O mais rápido ():

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

Alternativas (mais lentas):

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

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

Referência com as opções 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
        }
    }
}

Isso removerá todos os nós dentro do elemento.


... porque é complexo desnecessário, e nenhuma explicação é dada sobre como ele funciona (o que é realmente não óbvio), eu suspeito.
Periata Breatta

2

innerText é o vencedor! http://jsperf.com/innerhtml-vs-removechild/133 . Em todos os testes anteriores, o dom interno do nó pai foi excluído na primeira iteração e, em seguida, innerHTML ou removeChild quando aplicado à div vazia.


5
innerTexté uma coisa proprietária da MS embora. Apenas dizendo.
21411 DanMan

1
Certamente esse é um teste defeituoso - ele depende de boxestar disponível não como um var, mas através da pesquisa DOM com base no id, e como o whileloop faz essa chamada lenta muitas vezes é penalizada.
Nrabinowitz

2

Maneira mais simples de remover os nós filhos de um nó via Javascript

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

2

eu vi pessoas fazendo:

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

então alguém disse que usar el.lastNodeé mais rápido

no entanto, eu acho que este é o mais rápido:

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

O que você acha?

ps: este tópico foi um salva-vidas para mim. meu addon firefox foi rejeitado porque eu usei innerHTML. Tem sido um hábito há muito tempo. então eu digo isso. e eu realmente notei uma diferença de velocidade. ao carregar, o innerhtml demorou um pouco para atualizar, porém, passando addElement no seu instante!


1
Bem, eu acho que se é mais rápido ou não, alguns testes JSPerf pode provar qualquer caso
Nikos M.

super trabalho obrigado por esses testes. eles não incluem o for (var i=0...embora. Mas aqui é o que eu tenho quando eu corri esses testes: i.imgur.com/Mn61AmI.png também nesses três testes firstNode foi mais rápido do que LastNode e innerHTML que é muito legal
Noitidart

eu adicionei os testes: jsperf.com/innerhtml-vs-removechild/220 coisas interessantes a fazer método el.firstNode é a maneira mais rápida que parece
Noitidart

2

Usar um loop de alcance é o mais natural para mim:

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

De acordo com minhas medidas no Chrome e Firefox, é cerca de 1,3x mais lento. Em circunstâncias normais, isso talvez não importe.


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

1
Por favor, explique em resposta o que seu código está fazendo?
Nirav Joshi

1
É auto-explicativo
blacksheep

1

Geralmente, o JavaScript usa matrizes para fazer referência a listas de nós DOM. Portanto, isso funcionará bem se você tiver interesse em fazê-lo através da matriz HTMLElements. Além disso, vale a pena notar, porque estou usando uma referência de matriz em vez de proto JavaScript, isso deve funcionar em qualquer navegador, incluindo o IE.

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

1

Por que não estamos seguindo o método mais simples aqui "remova" em loop por dentro enquanto.

const foo = document.querySelector(".foo");
while (foo.firstChild) {
  foo.firstChild.remove();     
}
  • Selecionando a div principal
  • Usando o método "remove" dentro de um loop While para eliminar o primeiro elemento filho, até que não haja mais nenhum.

Por favor, explique suas linhas de código para que outros usuários possam entender sua funcionalidade. Obrigado!
Ignacio Ara

@IgnacioAra Done!
Neelansh Verma

1

Só vi alguém mencionar essa pergunta em outra e pensei em adicionar um método que ainda não vi:

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

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

A função clear está pegando o elemento e usando o nó pai para se substituir por uma cópia sem seus filhos. Não há muito ganho de desempenho se o elemento for escasso, mas quando o elemento possui vários nós, os ganhos de desempenho são realizados.


1
Na verdade, parece que foi mencionado aqui stackoverflow.com/a/15441725/576767
Félix Gagnon-Grenier

Felix Devo ter perdido isso, obrigado pela correção +1.
Harry Chilinguerian

1

Abordagem apenas funcional:

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

"childNodes" em domChildren fornecerá um nodeList dos elementos filhos imediatos, que fica vazio quando nenhum é encontrado. Para mapear sobre o nodeList, domChildren o converte em matriz. domEmpty mapeia uma função domRemove sobre todos os elementos que a remove.

Exemplo de uso:

domEmpty(document.body)

remove todos os filhos do elemento body.


0

Outras maneiras no jQuery

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

Baixa de votação porque o OP disse que as respostas no DOM direto são preferidas.
bwindels

Além disso, jQuery tem .empty()método, apenas $("#foo").empty()seria suficiente
YakovL

-1

simplesmente apenas o IE:

parentElement.removeNode(true);

true - significa fazer a remoção profunda - o que significa que todas as crianças também são removidas


1
IE? Este é o século 21!
everlasto

-1

O jeito mais fácil:

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

Enquanto isso funcionar, cuidado com o que container.innerHTML = nullfaria o Internet Explorer exibir o texto null. Então, isso realmente não remove as crianças, eu diria.
Arjan

-1

simples e rápido usando o loop for !!

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

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

isso não vai funcionar na <span>tag!


na acima de tudo solução nenhum código está removendo <span>tag
Mohit Karkar

-3

Se você quiser colocar algo de volta nisso div, innerHTMLprovavelmente é melhor.

Meu exemplo:

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

Se eu usar o método .firstChildou .lastChild, a displayHTML()função não funcionará depois, mas não haverá problema com o .innerHTMLmétodo.


-4

Este é um javascript puro, não estou usando jQuery, mas funciona em todos os navegadores, mesmo no IE, e é muito simples 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 ="";

})

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.