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


872

我将如何删除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答案,则需要加分。

Answers:


1674

选项1 A:清算innerHTML

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

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浏览器不会调用其HTML解析器,而是立即用单个#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 以防万一其检查速度firstChildlastChild(例如,如果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 -->)子级(但不保留其子级)-这在您的应用程序中可能是理想的(例如,某些使用内联HTML注释存储模板指令的模板系统)。
  • 直到最近几年才使用这种方法,因为Internet Explorer仅添加了lastElementChild对IE9的支持。

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猴子补丁:

  • 我们可以Element在JavaScript原型中添加一个新的方法属性,以简化对其的调用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>


6
在jQuery中,可能会考虑以下两个问题:此方法不仅删除子(和其他后代)元素,而且删除匹配元素集中的所有文本。这是因为根据DOM规范,元素内的任何文本字符串都被视为该元素的子节点。AND“为避免内存泄漏,jQuery在删除元素本身之前先从子元素中删除其他构造,例如数据和事件处理程序。”
jottos

102
顺便说一句,使用lastChild似乎更有效jsperf.com/innerhtml-vs-removechild/15
Andrey

24
仅当您处理HTML时,innerHTML才有效。如果内部只有SVG,则只有元素移除有效
stwissel 2013年

34
永远不要永远不要使用innerHTML =''。别。问题在于,这些项目看起来像已被删除,但在内部,节点仍然存在,从而降低了运行速度。由于某种原因,您必须删除所有单个节点。在Google Chrome和IE中都经过测试。请考虑删除innerHTML“解决方案”,因为它是错误的。
肯吉(Kenji)2013年

14
@vsync我不相信Kenji的评论是有根据的。这样做innerHTML = ''的速度较慢,但​​我不认为这是“错误的”。任何有关内存泄漏的主张都应有证据支持。
克里斯·米德尔顿

115

innerHTML正如m93a正确提到的那样,当前接受的答案关于速度变慢(至少在IE和Chrome中是错误的)是错误的。

Chrome和FF使用此方法的速度显着提高(这将破坏附加的jquery数据):

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

对于FF和Chrome来说,是遥不可及的,在IE中最快:

node.innerHTML = '';

InnerHTML 不会破坏您的事件处理程序或破坏jquery引用,也建议在此处将其作为解决方案:https : //developer.mozilla.org/zh-CN/docs/Web/API/Element.innerHTML

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

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

提到的其他方法似乎是可比较的,但是比例外的jquery(1.1.1和3.1.1)慢得多,它比innerHTML慢得多,后者比其他任何东西都慢得多:

$(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”,并且只有第一个迭代有要删除的节点,因此结果没有意义,在发布时,此线程中的测试设置不正确。


2
您的jsperf已完全损坏。那不是很好的证据。
bryc

4
这是目前最好的答案。该线程中的所有其他内容都是虚假信息(!)。感谢您清理所有那些不好的旧测试。
2015年

6
“ InnerHTML不会破坏您的事件处理程序,也不会弄乱任何jquery引用”,它会孤立其中的数据jQuery.cache,导致内存泄漏,因为唯一可用于管理该数据的引用是在您刚刚破坏的元素上。

1
“ InnerHTML不会破坏您的事件处理程序或弄乱任何jquery引用”是指容器,而不是子容器。cloneNode切换出容器,这意味着不维护对容器的引用(jQuery或其他方式)。如果您将jquery数据附加到jquery的缓存中,则可能值得使用jquery删除它们。希望他们在某个时候切换到WeakMaps。$ .cache的清理(甚至存在)似乎没有正式记录。
npjohns

10
jsperf测试在这里坏了。测试引擎报告以下内容:“错误:在迭代过程中未重新创建Dom节点,测试结果将毫无意义。”
senderle

73

使用现代Javascript,搭配remove

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

这是在ES5中写入删除节点的新方法。这是香草JS,读得多漂亮比以前的版本。

大多数用户应该拥有现代的浏览器,或者您可以根据需要进行转换。

浏览器支持 -2019年12月95%


14
因为parent.children / parent.childNodes是活动列表,所以for循环不起作用。调用remove会修改列表,因此不能保证您的迭代器会迭代所有内容。例如,在chrome中,您最终会跳过所有其他节点。最好使用while (parent.firstChild) { parent.removeChild(parent.firstChild); }循环。developer.mozilla.org/en-US/docs/Web/API/ParentNode/children developer.mozilla.org/en-US/docs/Web/API/Node/childNodes
2016年

3
while (parent.firstChild && !parent.firstChild.remove());
简捷的

1
当性能不是很重要时的[...parent.childNodes].forEach(el => el.remove());

1
这在Typescript中不起作用...而是使用while (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;
}

如果有可能让jQuery影响了后代,那么必须使用某种方法来清理jQuery数据。

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

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

如果仅使用DOM删除子项的方法,该数据将保留。


迄今为止,innerHTML方法是性能最高的。就是说,数据被jquery的.empty()清除:使用递归循环消除了与子标签相关联的jquery内的data()(速度很慢,但可能会显着减少内存使用),如果在未附加事件的情况下避免了内存泄漏jQuery,就像使用.onclick = ...一样。如果在子级中没有要删除的此类事件,则设置innerHTML不会泄漏。所以最好的答案是-这取决于。
克里斯·莫斯基尼

1
为什么不将firstChild表达式直接置于while循环的条件下?while ( myNode.firstChild ) {
维克多·扎曼尼安

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

36

如果您使用jQuery:

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

如果您不这样做:

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

20

最快的...

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

感谢Andrey Lushnikov链接到jsperf.com(非常酷的网站!)。

编辑:很明显,Chrome在firstChild和lastChild之间没有性能差异。最佳答案显示了一个很好的性能解决方案。


没有LINT警告的情况相同:clear:function(container){for(;;){var child = container.lastChild; 如果(!child)中断;container.removeChild(child); }
cskwg

9

如果只想让节点没有子节点,也可以像这样复制它:

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

取决于您要实现的目标。


3
也许您甚至可以遵循parent.replaceChild(cloned, original)?这可能比逐个删除子代更快,并且应该可以在支持DOM的所有组件上工作(因此,每种XML文档,包括SVG)都可以使用。设置innerHTML可能比逐个删除子代要快,但这只适用于HTML文档。应该测试一下。
马滕

13
不会复制使用添加的事件监听器addEventListener
马修

如果您可以忍受这种限制,那肯定很快:jsperf.com/innerhtml-vs-removechild/137
DanMan 2014年

7

这是另一种方法:

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
这很有趣,但是与其他方法相比似乎相当慢:jsperf.com/innerhtml-vs-removechild/256
Polaris878'1

6
element.textContent = '';

就像innerText一样,标准除外。它比一些removeChild(),但使用起来更容易,并且如果您没有太多要删除的内容,不会对性能造成太大影响。


文本节点不是元素
check_ca 2014年

抱歉,您是对的,它确实删除了所有子元素
check_ca 2014年

在旧版本的Internet Explorer中将无法使用此功能。
pwdst

4

为了回应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 );

我发现在替换元素内的字符串时此方法非常有用,如果您不确定节点将包含的内容,而不用担心如何清理混乱,请重新开始。


3
最糟糕的事情。如果要用其克隆替换原始节点,则将丢失对原始节点的引用。所有事件处理程序和其他绑定均无效。
阿伦·阿拉维德

4

这是我通常的工作:

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

瞧,以后您可以使用以下命令清空任何dom元素:

anyDom.empty()

我喜欢这个解决方案!我有什么理由要使用这个将overnerHTML设置为空白字符串?
TheCodenator

表现:domEl.innerHTML = ""很差,被认为是不好的作法
Ado Ren

3

有两种选择可以实现:

最快的 ():

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

替代方案(较慢):

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

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

具有建议选项的基准


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

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


...因为我怀疑这是不必要的复杂,并且没有给出关于它如何工作的解释(实际上并不明显)。
Periata Breatta '16

2

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


5
innerText是MS专有的东西。只是说。
2014年

1
可以肯定的是,这是一个有缺陷的测试-它依赖于box不是作为var而是通过基于id的DOM查找来提供,并且由于while循环使此缓慢的调用多次受到惩罚。
nrabinowitz

2

通过Java脚本删除节点的子节点的最简单方法

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

2

我看到人们在做:

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立即进行!


1
好吧,我认为是否最快,一些jsperf测试可以证明这两种情况-Nikos
M.

超级工作,感谢您的测试。他们不包括for (var i=0...一个。但是,这是我运行这些测试时得到的:i.imgur.com/Mn61AmI.png,因此在这三个测试中,firstNode的速度比lastNode和innerHTML的速度快,这真的很酷
Noitidart 2014年

我添加了测试:jsperf.com/innerhtml-vs-removechild/220做el.firstNode方法的有趣事情是看起来最快的方法
Noitidart 2014年

2

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

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

根据我在Chrome和Firefox中的测量,它慢了1.3倍。在正常情况下,这可能无关紧要。


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

1
请在回答中说明您的代码在做什么?
尼拉夫·乔希

1
它的自我解释
黑羊

1

通常,JavaScript使用数组来引用DOM节点列表。因此,如果您有兴趣通过HTMLElements数组进行此操作,则此方法会很好地工作。另外,值得注意的是,因为我使用的是数组引用而不是JavaScript原型,所以它可以在包括IE在内的任何浏览器中使用。

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

1

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

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

请解释您的代码行,以便其他用户可以理解其功能。谢谢!
伊格纳西奥(Ignacio Ara)'18年

@IgnacioAra完成!
Neelansh Verma,

1

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

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

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

clear函数采用元素并使用父节点将其自身替换为没有子元素的副本。如果元素稀疏,则性能提升不多,但是当元素具有一堆节点时,可以实现性能提升。


1
其实现在看来,这是在这里提到stackoverflow.com/a/15441725/576767
费利克斯·加侬-格雷尼尔

费利克斯(Felix)我一定很想念它,谢谢您的+1。
哈里·奇林格里安

1

仅功能方法:

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元素移除所有子元素。


0

jQuery中的其他方式

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

拒绝投票,因为OP首选直接DOM中的答案。
bwindels

另外,jQuery有.empty()方法,就$("#foo").empty()足够了
YakovL

-1

仅仅是IE:

parentElement.removeNode(true);

true -意味着进行深层移除-这意味着所有孩子也都被移除了


1
IE浏览器?这是21世纪!
everlasto

-1

最简单的方法:

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

在执行此操作时,请当心container.innerHTML = null使Internet Explorer显示文本null。因此,我要说这并不能真正消除孩子们。
Arjan

-1

简单快速地使用for循环!

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

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

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


在所有解决方案中,没有代码可以删除<span>标签
Mohit Karkar

-3

如果您想放些东西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>

如果我使用.firstChildor .lastChild方法,该displayHTML()函数此后将无法正常工作,但是该.innerHTML方法没有问题。


-4

这是一个纯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 ="";

})

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.