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

1347

我该如何使用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;如果您提供了纯DOM答案以及jQuery答案,则会获得额外加分。

39个回答

8

这里还有一种方法:

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

8
element.textContent = '';

它类似于innerText,但更加标准。它比removeChild()略慢一些,但更易于使用,如果你要删除的内容不太多,它不会对性能产生太大影响。参考链接:http://jsperf.com/so-remove-children

文本节点不是元素。 - check_ca
抱歉,你是对的,它确实会删除所有子元素。 - check_ca
这在旧版本的Internet Explorer中无法运行。 - pwdst

6

这是我通常做的:

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

然后,您可以使用以下方法清空任何DOM元素:

anyDom.empty()

我喜欢这个解决方案!有什么理由让我使用它而不是将innerHTML设置为空字符串? - Full Stack Alien
性能:domEl.innerHTML = "" 的表现较差,被认为是不良实践。 - Ado Ren

6
一行代码循环迭代地从 DOM 中移除所有 node 的子元素。
Array.from(node.children).forEach(c => c.remove())

或者

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

5
作为对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
最糟糕的事情就是,如果你用克隆节点替换原始节点,那么你将失去对原始节点的引用。任何事件处理程序和其他绑定都将失效。 - Arun Aravind

5

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

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

根据我在Chrome和Firefox中的测量,它大约慢了1.3倍。在正常情况下,这可能并不重要。


5

我已经审查了所有问题,并决定测试最关键的测试用例性能:

  • 我们需要清除内容
  • 我们需要保留原始元素
  • 我们需要删除许多子元素

因此,我的HTML代码是:

<div id="target">
  <div><p>Title</p></div>
  <!-- 1000 at all -->
  <div><p>Title</p></div>
</div>

// innerHTML
target.innerHTML = "";

// lastChild
while (target.hasChildNodes()) {
    target.removeChild(target.lastChild);
}

// firstChild
while (target.hasChildNodes()) {
    target.removeChild(target.firstChild);
}

// replaceChildren
target.replaceChildren();
结果:
Checked test: innerHTML x 1,222,273 ops/sec ±0.47% (63 runs sampled)
Checked test: lastChild x 1,336,734 ops/sec ±0.87% (65 runs sampled)
Checked test: firstChild x 1,313,521 ops/sec ±0.74% (64 runs sampled)
Checked test: replaceChildren x 743,076 ops/sec ±1.08% (53 runs sampled)

Checked test: innerHTML x 1,202,727 ops/sec ±0.83% (63 runs sampled)
Checked test: lastChild x 1,360,350 ops/sec ±0.72% (65 runs sampled)
Checked test: firstChild x 1,348,498 ops/sec ±0.78% (63 runs sampled)
Checked test: replaceChildren x 743,076 ops/sec ±0.86% (53 runs sampled)

Checked test: innerHTML x 1,191,838 ops/sec ±0.73% (62 runs sampled)
Checked test: lastChild x 1,352,657 ops/sec ±1.42% (63 runs sampled)
Checked test: firstChild x 1,327,664 ops/sec ±1.27% (65 runs sampled)
Checked test: replaceChildren x 754,166 ops/sec ±1.88% (61 runs sampled)
结论

现代API最慢。第一个子元素和最后一个子元素的方式是相等的,如果使用多个情况进行比较,则可能存在一些副作用。但是并排放置可以看到:

Checked test: firstChild x 1,423,497 ops/sec ±0.53% (63 runs sampled)
Checked test: lastChild x 1,422,560 ops/sec ±0.36% (66 runs sampled)

Checked test: firstChild x 1,368,175 ops/sec ±0.57% (65 runs sampled)
Checked test: lastChild x 1,381,759 ops/sec ±0.39% (66 runs sampled)

Checked test: firstChild x 1,372,109 ops/sec ±0.37% (66 runs sampled)
Checked test: lastChild x 1,355,811 ops/sec ±0.35% (65 runs sampled)

Checked test: lastChild x 1,364,830 ops/sec ±0.65% (64 runs sampled)
Checked test: firstChild x 1,365,617 ops/sec ±0.41% (65 runs sampled)

Checked test: lastChild x 1,389,458 ops/sec ±0.50% (63 runs sampled)
Checked test: firstChild x 1,387,861 ops/sec ±0.40% (64 runs sampled)

Checked test: lastChild x 1,388,208 ops/sec ±0.43% (65 runs sampled)
Checked test: firstChild x 1,413,741 ops/sec ±0.47% (65 runs sampled)

附言:浏览器:Firefox 111.0.1


4
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

4

element.innerHTML = ""(或.textContent)是迄今为止最快的解决方案。

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

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

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

最后,不要使用node.parentNode.replaceChild(node.cloneNode(false), node)。这将用它自己的副本替换节点而不包括其子项。但是,这不会保留事件侦听器并且会中断对该节点的其他引用。


4
有几种实现它的选项:
最快的方法():
while (node.lastChild) {
  node.removeChild(node.lastChild);
}

替代方案(速度较慢):

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

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

Benchmark with the suggested options


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接