簡體   English   中英

在 JavaScript 中刪除 DOM 節點的所有子元素

[英]Remove all child elements of a DOM node in JavaScript

我將如何着手刪除 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; 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以防萬一檢查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 --> )的父級(但不是它們的后代)的子級 - 這在您的應用程序中可能是可取的(例如,一些使用內聯 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; 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任何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>

在 2022+ 年,使用replaceChildren() API!

現在可以使用(支持跨瀏覽器的) replaceChildren API替換所有子項:

container.replaceChildren(...arrayOfNewChildren);

這將同時做到:

  • 刪除所有現有的孩子,和
  • 在一次操作中附加所有給定的新子代。

您還可以使用相同的 API 來刪除現有的子項,而不替換它們:

container.replaceChildren();

Chrome/Edge 86+、Firefox 78+ 和 Safari 14+ 完全支持此功能。 這是完全指定的行為。 這可能比這里提出的任何其他方法都要,因為刪除舊子項和添加新子項是在不需要innerHTML的情況下完成的,並且是一步而不是多個。

使用現代 Javascript,帶有remove

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

這是在 ES5 中編寫節點刪除的新方法。 它是普通的 JS,比依賴父級讀起來好得多。

支持所有現代瀏覽器。

瀏覽器支持- 97% 21 年 6 月

正如 m93a 正確提到的那樣,當前接受的關於innerHTML速度較慢的答案是錯誤的(至少在 IE 和 Chrome 中)。

使用這種方法(這將破壞附加的 jquery 數據),Chrome 和 FF 的速度要快得多:

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/300 https://jsperf.com/remove-all-child-elements-of-a- dom-node-in-javascript (jsperf 重新啟動的新 url,因為編輯舊 url 不起作用)

Jsperf 的“per-test-loop”通常被理解為“per-iteration”,只有第一次迭代有節點要刪除,所以結果沒有意義,在發布時,這個線程中的測試設置不正確。

如果你使用 jQuery:

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

如果您不這樣做:

var foo = document.getElementById('foo');
while (foo.firstChild) foo.removeChild(foo.firstChild);
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方法刪除子項,則該數據將保留。

最快的...

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

感謝 Andrey Lushnikov 鏈接到 jsperf.com (很酷的網站!)。

編輯:需要明確的是,在 Chrome 中 firstChild 和 lastChild 之間沒有性能差異。 最佳答案顯示了一個很好的性能解決方案。

使用elm.replaceChildren()

它在沒有廣泛支持的情況下是實驗性的,但是在沒有參數的情況下執行時會滿足您的要求,並且比遍歷每個子節點並刪除它更有效。 如前所述,用空字符串替換innerHTML將需要瀏覽器進行 HTML 解析。

MDN 文檔

更新它現在被廣泛支持

如果您只想擁有沒有子節點的節點,您也可以像這樣復制它:

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

取決於您要達到的目標。

這是另一種方法:

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

Ecma6 讓它變得簡單而緊湊

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

這回答了問題,並刪除了“所有子節點”

如果存在屬於myNode的文本節點,則無法使用 CSS 選擇器選擇它們,在這種情況下,我們必須應用(也):

myNode.textContent = '';

實際上最后一個可能是最快和更有效/高效的解決方案。

.textContent.innerText.innerHTML更有效,參見: MDN

element.textContent = '';

就像innerText,除了標准。 它比removeChild()一點,但它更容易使用,如果你沒有太多要刪除的東西,它不會對性能產生太大影響。

這是我通常做的:

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

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

anyDom.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 );

這也適用於嘗試訪問 parentNode 時返回 null 的不在 dom 中的節點。 此外,如果您需要確保在添加內容之前節點為空,這確實很有幫助。 考慮下面的用例。

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

我發現這種方法在替換元素內的字符串時非常有用,如果您不確定節點將包含什么,而不是擔心如何清理混亂,重新開始。

使用范圍循環對我來說是最自然的:

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

根據我在 Chrome 和 Firefox 中的測量,它慢了大約 1.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
        }
    }
}

這將刪除元素內的所有節點。

有幾種選擇可以實現這一目標:

最快的 ():

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

替代品(較慢):

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

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

使用建議的選項進行基准測試

innerText 是贏家! http://jsperf.com/innerhtml-vs-removechild/133 在所有先前的測試中,父節點的內部 dom 在第一次迭代時被刪除,然后將 innerHTML 或 removeChild 應用於空 div。

通過Javascript刪除節點的子節點的最簡單方法

var myNode = document.getElementById("foo");
while(myNode.hasChildNodes())
{
   myNode.removeChild(myNode.lastChild);
}
 let el = document.querySelector('#el');
 if (el.hasChildNodes()) {
      el.childNodes.forEach(child => el.removeChild(child));
 }

我看到人們在做:

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 會立即更新!

為什么我們不遵循這里最簡單的方法“刪除”在while中循環。

const foo = document.querySelector(".foo");
while (foo.firstChild) {
  foo.firstChild.remove();     
}
  • 選擇父 div
  • 在 While 循環中使用“刪除”方法來消除第一個子元素,直到沒有剩余。

通常,JavaScript 使用數組來引用 DOM 節點列表。 因此,如果您有興趣通過 HTMLElements 數組進行操作,這將很好地工作。 另外,值得注意的是,因為我使用的是數組引用而不是 JavaScript 原型,所以這應該適用於任何瀏覽器,包括 IE。

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

剛剛看到有人在另一個中提到這個問題,並認為我會添加一個我還沒有看到的方法:

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

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

clear 函數獲取元素並使用父節點將自身替換為沒有子節點的副本。 如果元素稀疏,性能提升不大,但是當元素有一堆節點時,性能提升就會實現。

僅功能方法:

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

element.innerHTML = "" (或 .textContent)是迄今為止最快的解決方案

這里的大多數答案都是基於有缺陷的測試

例如: https ://jsperf.com/innerhtml-vs-removechild/15
此測試不會在每次迭代之間向元素添加新的子元素。 第一次迭代將刪除元素的內容,然后其他所有迭代都不會執行任何操作。 在這種情況下, while (box.lastChild) box.removeChild(box.lastChild)更快,因為box.lastChild 99% 的時間都是null

這是一個適當的測試: https ://jsperf.com/innerhtml-conspiracy

最后,不要使用node.parentNode.replaceChild(node.cloneNode(false), node) 這將用沒有其子節點的自身副本替換節點。 但是,這不會保留事件偵聽器並破壞對節點的任何其他引用。

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的元素

您可以從容器中刪除所有子元素,如下所示:

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

現在您可以調用該函數並傳遞選擇器,如下所示:

如果元素有 id = foo

emptyDom('#foo');

如果元素有 class = foo

emptyDom('.foo');

如果元素有標簽 = < div >

emptyDom('div')

如果你想清空整個父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>

我是這樣做的

 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 中刪除節點的所有子node的單行代碼

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

只是IE:

parentElement.removeNode(true);

true - 表示進行深度移除 - 這意味着所有子項也被移除

我將如何在 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 的答案,則可以加分。

使用 for 循環簡單快速!!

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

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

這在<span>標簽中不起作用!

jQuery中的其他方式

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

如果您想將某些內容放回該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方法沒有問題。

這是一個純 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 ="";

})

用 jQuery :

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM