如何在JavaScript中删除DOM节点的所有子元素?

假设我有以下(丑陋的)HTML:

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

然后我像这样获取我想要的节点

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

我怎么能删除foo的孩子,这样就<p id="foo"></p>是剩下的?

我能不能这样做:

myNode.childNodes = new Array();

或者我应该使用一些组合的removeElement?

我希望答案是直接的DOM;不过,如果你在jQuery中提供了一个dom专用的答案,会有额外的加分。


选项1 A:清除innerHTML。

这种方法很简单,但可能不适合高性能应用程序,因为它调用浏览器的HTML解析器(尽管浏览器可能针对值为空字符串的情况进行优化)。

doFoo。Onclick = () => { const myNode = document.getElementById("foo"); myNode。innerHTML = "; } <div id='foo' style="height: 100px;宽度:100 px;边框:1px纯黑色;" > < span >你好> < /跨度 < / div > <button id='doFoo'>通过innerHTML删除</button>

选项1 B:清除文本内容

如上所述,但是使用. textcontent。根据MDN,这将比innerHTML更快,因为浏览器不会调用他们的HTML解析器,而是立即用单个#text节点替换元素的所有子元素。

doFoo。Onclick = () => { const myNode = document.getElementById("foo"); myNode。textContent = "; } <div id='foo' style="height: 100px;宽度:100 px;边框:1px纯黑色;" > < span >你好> < /跨度 < / div > <按钮id='doFoo'>删除通过textContent</按钮>

选项2 A:循环删除每个lastChild:

这个答案的早期编辑使用了firstChild,但在计算机科学中更新为使用lastChild,一般来说,删除集合的最后一个元素比删除第一个元素要快得多(这取决于集合是如何实现的)。 循环继续检查firstChild,以防检查firstChild比检查lastChild快(例如,如果元素列表被UA实现为有向链表)。

doFoo。Onclick = () => { const myNode = document.getElementById("foo"); while (myNode.firstChild) { myNode.removeChild (myNode.lastChild); } } <div id='foo' style="height: 100px;宽度:100 px;边框:1px纯黑色;" > < span >你好> < /跨度 < / div > <button id='doFoo'>通过lastChild-loop删除</button>

选项2 B:循环删除每个lastElementChild:

这种方法保留了所有非element(即#text节点和<!)——comments——>)父类的子类(但不是它们的后代)——这可能在你的应用程序中是可取的(例如,一些模板系统使用内联HTML注释来存储模板指令)。 这种方法直到最近几年才被使用,因为Internet Explorer只在IE9中添加了对lastElementChild的支持。

doFoo。Onclick = () => { const myNode = document.getElementById("foo"); while (myNode.lastElementChild) { myNode.removeChild (myNode.lastElementChild); } } <div id='foo' style="height: 100px;宽度:100 px;边框:1px纯黑色;" > <!——这条评论不会被删除——> < span > <你好!-></span> . -></span> . - <!——但这只不会。--> < / div > <button id='doFoo'>通过lastElementChild-loop删除</button>

好处:元素。clearChildren monkey-patch:

我们可以在JavaScript中的Element原型中添加一个新的method-property,以简化对它的调用,只调用el. clearchildren()(其中el是任何HTML元素对象)。 (严格地说,这是一个猴子补丁,而不是一个polyfill,因为这不是一个标准的DOM特性或缺失的特性。请注意,在许多情况下,不鼓励打猴子补丁。)

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>


用jQuery:

$("#foo").find("*").remove();

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

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

如果您的后代有可能受到jQuery影响,那么您必须使用一些方法来清理jQuery数据。

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

jQuery .empty()方法将确保任何与被删除的元素相关联的jQuery数据将被清除。

如果您只是使用DOM方法删除子元素,那么这些数据将会保留。


如果你使用jQuery:

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

如果你没有:

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

如果你只想拥有这个节点而不拥有它的子节点,你也可以像这样复制它:

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

这取决于你想要达到什么目的。


jQuery的其他方法

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

作为对DanMan, Maarten和Matt的回应。克隆一个节点,以设置文本确实是一个可行的方法,在我的结果。

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

这也适用于不在dom中的节点,当试图访问parentNode时返回null。此外,如果您需要在添加内容之前确保节点是空的,这是非常有用的。考虑下面的用例。

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

我发现这个方法在替换元素中的字符串时非常有用,如果你不确定节点将包含什么,与其担心如何清理混乱,不如从头开始。


最快的…

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

感谢Andrey Lushnikov提供的jsperf.com链接(很酷的网站!)

编辑:需要明确的是,在Chrome中,firstChild和lastChild之间没有性能差异。上面的答案显示了一个很好的性能解决方案。


innerText是赢家!http://jsperf.com/innerhtml-vs-removechild/133。在所有之前的测试中,父节点的内部dom在第一次迭代时被删除,然后innerHTML或removecchild应用于空div。


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

这将删除元素中的所有节点。


我看到人们这样做:

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

然后有人说用el。lastNode更快

然而,我认为这是最快的:

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

你怎么看?

ps: 这个话题是我的救命稻草。我的firefox插件被拒绝,因为我使用了innerHTML。这是很长一段时间以来的习惯。然后我发现了这个。我注意到了速度上的差异。加载innerhtml需要一段时间来更新,然而通过addElement它的瞬间!


这是另一种方法:

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

element.textContent = '';

它像innerText,除了标准。它比removecchild()稍微慢一点,但更容易使用,如果没有太多东西需要删除,则不会有太大的性能差异。


目前公认的答案是错误的innerHTML更慢(至少在IE和Chrome),正如m93a正确提到的。

Chrome和FF使用这种方法会显着更快(这将破坏附加的jquery数据):

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

FF和Chrome远远排在第二,IE最快:

node.innerHTML = '';

InnerHTML不会破坏你的事件处理程序或打破jquery引用,它也建议作为一个解决方案在这里: https://developer.mozilla.org/en-US/docs/Web/API/Element.innerHTML。

最快的DOM操作方法(仍然比前两个慢)是范围移除,但是直到IE9才支持范围。

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

上面提到的其他方法看起来差不多,但比innerHTML慢很多,除了jquery(1.1.1和3.1.1),它比其他任何方法都要慢得多:

$(node).empty();

证据:

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 (重新启动jsperf的新url,因为编辑旧url不工作)

Jsperf的“per-test-loop”通常被理解为“per-iteration”,只有第一次迭代需要删除节点,所以结果是没有意义的,在发布的时候,这个线程中的测试设置不正确。


通过Javascript删除节点的子节点的最简单方法

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

使用现代Javascript,与删除!

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

这是在ES5中编写节点删除的一种较新的方法。它是香草的JS,读起来比依赖父母要好得多。

支持所有现代浏览器。

浏览器支持- 97% 21年6月


简单的只有IE:

parentElement.removeNode(true);

True -意味着做深层移除-这意味着所有的子也被移除


有几个选项可以实现这一目标:

最快的():

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

选择(慢):

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

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

使用建议的选项进行基准测试


这是一个纯粹的javascript,我不使用jQuery,但工作在所有浏览器甚至IE,这是非常简单的理解

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

})

通常,JavaScript使用数组引用DOM节点列表。如果你有兴趣通过HTMLElements数组来做,这会很好。另外,值得注意的是,因为我使用的是数组引用而不是JavaScript原型,这应该可以在任何浏览器中工作,包括IE。

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

如果你想把一些东西放回那个div, innerHTML可能更好。

我的例子:

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

如果我使用. firstchild或. lastchild方法,displayHTML()函数之后不会工作,但使用. innerhtml方法没有问题。


为什么我们不在这里使用最简单的方法“remove”循环在while中。

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

选择父div 在While循环中使用“remove”方法消除第一个子元素,直到没有剩余。


简单快速使用for循环!!

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

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

这将在<span>标签不起作用!


以下是我通常做的事情:

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

瞧,之后你可以清空任何dom元素:

anyDom.empty()

刚刚看到有人在另一个问题中提到这个问题,我想我会添加一个我还没有看到的方法:

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

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

clear函数获取元素并使用父节点将其自身替换为没有其子节点的副本。如果元素是稀疏的,则性能增益不大,但当元素有一堆节点时,性能增益就实现了。


使用range循环对我来说是最自然的:

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

根据我在Chrome和Firefox中的测量,它要慢1.3倍左右。在正常情况下,这或许无关紧要。


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

仅函数方法:

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

domChildren中的"childNodes"将给出一个直接子元素的nodeList,如果没有找到则为空。为了在nodeList上进行映射,domChildren将它转换为数组。domEmpty将函数domRemove映射到所有移除它的元素上。

使用示例:

domEmpty(document.body)

从body元素中移除所有子元素。


你可以从一个容器中删除所有子元素,如下所示:

function emptyDom(selector){
 const elem = document.querySelector(selector);
 if(elem) elem.innerHTML = "";
}

现在你可以调用函数并像下面这样传递选择器:

如果元素id = foo

emptyDom('#foo');

如果元素有class = foo

emptyDom('.foo');

如果元素有tag = <div>

emptyDom('div')

使用elm.replaceChildren()。

它是实验性的,没有广泛的支持,但当不带参数执行时,它会做你所要求的,而且它比遍历每个子节点并删除它更有效。如前所述,用空字符串替换innerHTML将需要浏览器进行HTML解析。

MDN文档

它现在得到了广泛的支持


元素。innerHTML = ""(或.textContent)是目前为止最快的解决方案

这里的大多数答案都是基于有缺陷的测试

例如: https://jsperf.com/innerhtml-vs-removechild/15 该测试不会在每次迭代之间向元素添加新的子元素。第一次迭代将删除元素的内容,然后每一次迭代都不执行任何操作。 在这种情况下,while (box. lastchild) box. removecchild (box. lastchild)更快,因为box. lastchild。lastChild在99%的情况下为null

这里有一个合适的测试:https://jsperf.com/innerhtml-conspiracy

最后,不要使用node. parentnode . replacechild (node. clonenode (false), node)。这将用节点本身的副本替换它的子节点。但是,这不会保留事件侦听器,并破坏对该节点的任何其他引用。


Ecma6使它简单而紧凑

myNode.querySelectorAll('*').forEach( n => n.remove() );

这回答了问题,并删除了“所有子节点”。

如果有属于myNode的文本节点,它们不能被CSS选择器选中,在这种情况下,我们必须应用(also):

myNode.textContent = '';

实际上,最后一个可能是最快和更有效的解决方案。

.textContent比.innerText和.innerHTML更有效,参见:MDN


2022年+,用替代品!

现在可以使用(跨浏览器支持的)replaceChildren API替换所有的子程序:

container.replaceChildren(...arrayOfNewChildren);

这可以做到这两点:

删除所有现有的子节点,和 在一个操作中追加所有给定的新子节点。

你也可以使用相同的API来删除现有的子节点,而不替换它们:

container.replaceChildren();

这在Chrome/Edge 86+, Firefox 78+和Safari 14+中完全支持。它是完全指定的行为。这可能比这里提出的任何其他方法都要快,因为删除旧的子元素和添加新的子元素不需要innerHTML,而且是一步完成的,而不是多个步骤。


如果你想清空整个父DOM,那么很简单…

只需使用.empty()

函数removeAll() { $(' #父母')空虚(); } < script src = " https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js " > < /脚本> <按钮onclick = " removeAll ()>删除parent</button的所有元素 < div id = "父" > 标题< h3 > < / h3 > 儿童1 < p > < / p > 儿童2 < p > < / p > 孩子< p > 3 < / p > < / div >


ES6+浏览器(2016年之后发布的主要浏览器)的最佳删除方法:

也许有很多方法可以做到这一点,例如Element.replaceChildren()。 我想向您展示一个有效的解决方案,只有一个重绘和回流,支持所有ES6+浏览器。

function removeChildren(cssSelector, parentNode){
    var elements = parentNode.querySelectorAll(cssSelector);
    let fragment = document.createDocumentFragment(); 
    fragment.textContent=' ';
    fragment.firstChild.replaceWith(...elements);
}

用法:removeChildren('.foo',document.body);:删除<body>中所有className为foo的元素


我是这样做的

data = { "messages": [ [0, "userName", "message test"], [1, "userName1", "message test1"] ] } for (let i = 0; i < data.messages.length; i++) { var messageData = document.createElement('span') messageData.className = 'messageClass' messageData.innerHTML = `${data.messages[i][2]} ${'<br />'}` $(".messages").append(messageData) } $('#removeMessages').on('click', () => { node = $('.messages').get(0) while (node.firstChild) { node.firstChild.remove() } $('#removeMessages').toggle(500) $('#addMessages').toggle(500) }) $('#addMessages').on('click', () => { for (let i = 0; i < data.messages.length; i++) { var messageData = document.createElement('span') messageData.className = 'messageClass' messageData.innerHTML = `${data.messages[i][2]} ${'<br />'}` $(".messages").append(messageData) } $('#addMessages').toggle(500) $('#removeMessages').toggle(500) }) #addMessages{ display:none; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <div class="messages"></div> <button type='button' id="removeMessages">Remove</button> <button type='button' id="addMessages">Add MEssages</button>


迭代地从DOM中删除节点的所有子节点的一行程序

Array.from(node.children).forEach(c => c.remove())

Or

[...node.children].forEach(c => c.remove())