如何在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专用的答案,会有额外的加分。


当前回答

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它的瞬间!

如果你使用jQuery:

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

如果你没有:

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

作为对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 );

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

仅函数方法:

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

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