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

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

假设我有以下(丑陋的)超文本标记语言:

<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的答案,则会加分。

1351619 次浏览

选项1 A:清除innerHTML

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

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>

选项1 B:清除textContent

  • 如上所述,但是使用.textContent.根据MDN这将比innerHTML更快,因为浏览器不会调用其超文本标记语言解析器,而是会立即用单个#text节点替换元素的所有子节点。

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>

选项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; width: 100px; border: 1px solid black;"><span>Hello</span></div><button id='doFoo'>Remove via lastChild-loop</button>

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

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

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>

奖励:Element.clearChildren猴子补丁:

  • 我们可以在JavaScript中的Element原型中添加一个新的方法属性,以简化对el.clearChildren()的调用(其中el任何超文本标记语言元素对象)。
  • (严格来说,这是一个猴子补丁,而不是一个多边形,因为这不是一个标准的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#0方法将确保与被删除的元素关联的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();//orfoo.html("");//orfoo.empty();

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

// @param {node} node// @return {node} empty nodefunction removeAllChildrenFromNode (node) {var shell;// do not copy the contentsshell = node.cloneNode(false);
if (node.parentNode) {node.parentNode.replaceChild(shell, node);}
return shell;}
// use as suchvar myNode = document.getElementById('foo');myNode = removeAllChildrenFromNode( myNode );

这也适用于不在dom中的节点,当尝试访问父节点时返回null。此外,如果您需要安全,在添加内容之前节点为空,这真的很有帮助。考虑下面的用例。

// @param {node} node// @param {string|html} content// @return {node} node with content onlyfunction refreshContent (node, content) {var shell;// do not copy the contentsshell = node.cloneNode(false);
// use innerHTML or you preffered method// depending on what you needshell.innerHTML( content );
if (node.parentNode) {node.parentNode.replaceChild(shell, node);}
return shell;}
// use as suchvar 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上没有性能差异。上面的答案显示了一个很好的性能解决方案。

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

var empty_element = function (element) {
var node = element;
while (element.hasChildNodes()) {              // selected elem has children
if (node.hasChildNodes()) {                // current node has childrennode = node.lastChild;                 // set current node to child}else {                                     // last child foundconsole.log(node.nodeName);node = node.parentNode;                // set node to parentnode.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 objectvar rangeObj = new Range();
// Select all of theParent's childrenrangeObj.selectNodeContents(theParent);
// Delete everything that is selectedrangeObj.deleteContents();}
element.textContent = '';

它类似于innerText,除了标准。它是有点慢而不是removeChild(),但它更容易使用,如果您没有太多要删除的内容,它不会产生太大的性能差异。

目前接受的答案是错误的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操作方法(仍然比前两种慢)是Range移除,但直到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/300https://jsperf.com/remove-all-child-elements-of-a-dom-node-in-javascript(因为编辑旧的url不起作用,所以重新启动jspef的新url)

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

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

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

使用现代Javascript,remove

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

这是在ES5中编写节点删除的新方法。它是vanilla JS,读取比依赖父级更好。

支持所有现代浏览器。

浏览器支持-97%

只是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 allfor(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]);}

如果你想把一些东西放回divinnerHTML可能更好。

我的例子:

<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方法没有问题。

为什么我们不遵循这里最简单的方法“删除”里面的循环。

const foo = document.querySelector(".foo");while (foo.firstChild) {foo.firstChild.remove();}
  • 选择父div
  • 使用“删除”方法在一个虽然循环中消除第一个子元素,直到没有留下。

简单快速地使用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函数是获取元素并使用父节点用没有子节点的副本替换自己。如果元素是稀疏的,性能增益不会很大,但当元素有一堆节点时,性能增益会实现。

使用范围循环对我来说感觉最自然:

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中将给出一个包含直接子元素的nodeList,当没有找到子元素时,该元素为空。为了映射到nodeList上,domChildren将其转换为数组。dom空将一个函数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');

如果元素有标签=<div>

emptyDom('div')

使用elm.replaceChildren()

它是实验性的,没有广泛的支持,但是当在没有参数的情况下执行时,它将满足您的要求,并且它比循环遍历每个子级并将其删除更有效。如前所述,用空字符串替换innerHTML将需要浏览器部分的超文本标记语言解析。

MDN文档

更新现在得到了广泛的支持

element.innerHTML = ""(或. text Content)是到目前为止最快的解决方案

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

例如:https://jsperf.com/innerhtml-vs-removechild/15
此测试不会在每次迭代之间向元素添加新的子元素。第一次迭代将删除元素的内容,然后每隔一次迭代都不会做任何事情。在这种情况下,while (box.lastChild) box.removeChild(box.lastChild)更快,因为box.last孩子是#1 99%的时间

这是一个正确的测试:https://jsperf.com/innerhtml-conspiracy

最后,不要使用node.parentNode.replaceChild(node.cloneNode(false), node)。这将用没有其子节点的自身副本替换节点。但是,这不会保留事件侦听器并中断对节点的任何其他引用。

Ecma6使其简单而紧凑

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

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

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

myNode.textContent = '';

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

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

2022年+,使用replaceChildren() API!

现在可以使用(支持跨浏览器)#0接口替换所有子级:

container.replaceChildren(...arrayOfNewChildren);

这将做两个:

  • 删除所有现有的儿童,并
  • 在一次操作中追加所有给定的新子级。

您还可以使用相同的API来删除现有的子级,而无需替换它们:

container.replaceChildren();

Chrome /Edge86+,Firefox 78+和Safari14+完全支持。这是完全规定的行为。这很可能是更快,而不是这里提出的任何其他方法,因为删除旧孩子和添加新孩子是在不需要innerHTML的情况下完成的,并且是在一个步骤中而不是在多个步骤中完成的。

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

使用.empty()

function removeAll() {$('#parent').empty();}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><button onclick="removeAll()">Remove all the element of parent</button><div id="parent"><h3>Title</h3><p>Child 1</p><p>Child 2</p><p>Child 3</p></div>

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

也许有很多方法可以做到这一点,例如Element.replace儿童()。我想向您展示一个有效的解决方案只有一次重绘和回流支持所有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>中类名称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>