Видаліть усі дочірні елементи вузла DOM в JavaScript


872

Як би я вирішив видалити всі дочірні елементи вузла DOM в JavaScript?

Скажіть, у мене є такий (некрасивий) 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.

Відповіді:


1674

Варіант 1 А: Розчищення 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 Б: Розміщення 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 лише додав підтримку 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 об'єкт елемента).
  • (Строго кажучи, це патч мавп, а не полів, оскільки це не є стандартною функцією 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, будь-який рядок тексту в межах елемента вважається дочірнім вузлом цього елемента. ТА "Щоб уникнути витоку пам'яті, jQuery видаляє інші конструкції, такі як обробники даних та події, з дочірніх елементів, перш ніж видаляти самі елементи."
Jottos

104
Btw, використовуючи lastChild, здається, трохи ефективнішим jsperf.com/innerhtml-vs-removechild/15
Андрій Лушников

24
innerHTML працює лише в тому випадку, якщо ви маєте справу лише з HTML. Якщо є, наприклад, SVG, працює лише видалення елемента
stwissel

34
НІКОЛИ НІКОЛИ не використовуйте innerHTML = ''. Не варто. Проблема полягає в тому, що елементи виглядають як видалені, але всередині вузли зберігаються і сповільнюються. Вам потрібно видалити всі окремі вузли з причини. Тестовано як в Google Chrome, так і в IE. Будь ласка, подумайте про видалення внутрішньогоHTML "рішення", оскільки це неправильно.
Кенджі

15
@vsync Я не вважаю, що коментар Кенджі є обґрунтованим. Робити innerHTML = ''це повільніше, але я не думаю, що це "неправильно". Будь-які претензії на витоки пам'яті повинні бути підкріплені доказами.
Кріс Міддлтон

115

В даний час прийнята відповідь неправильна щодо 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 (ще повільніше, ніж попередні два) - це видалення діапазону, але діапазони не підтримуються до IE9.

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

Інші згадані способи здаються порівнянними, але набагато повільнішими, ніж внутрішні HTML, за винятком зовнішнього, 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 (новий URL для перезавантаження jsperf, оскільки редагування старої URL-адреси не працює)

Jsperf "per-test-цикл" часто розуміється як "за повторною ітерацією", і лише перша ітерація має вузли для видалення, тому результати є безглуздими, під час публікації в цій темі були неправильно встановлені тести.


2
Ваш jsperf повністю зламаний. Це не дуже хороші докази.
bryc

4
Наразі це найкраща відповідь. Все інше в цій темі - це дезінформація (!) Спасибі за очищення всіх тих поганих старих тестів.
Бен

6
"InnerHTML не знищить ваші обробники подій і не зіпсує жодних посилань на jquery". Це осиротіть дані, що містяться jQuery.cache, спричиняючи протікання пам'яті, оскільки для керування цими даними є лише посилання на елементи, які ви тільки що знищили.

1
"InnerHTML не знищить ваші обробники подій і не зіпсує жодних посилань на jquery", стосується контейнера, а не дітей. cloneNodeвимикає контейнер, що означає, що посилання на контейнер не підтримуються (jquery чи іншим чином). Кеш Jquery може змусити використовувати jquery для видалення елементів, якщо до них додані дані jquery. Сподіваємось, вони переходять на слабкі карти в якийсь момент. Чищення (або навіть існування) $ .cache, здається, не є офіційно зафіксованим.
npjohns

10
Тут пробиваються тести jsperf. Тестовий двигун повідомляє про таке: "Помилка: вузли Dom не відтворюються під час ітерацій, результати тестування будуть безглуздими."
senderle

73

Використовуйте сучасний Javascript, з remove!

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

Це більш новий спосіб запису видалення вузла в ES5. Він ванільний JS і читає набагато приємніше, ніж попередні версії.

Більшість користувачів повинні мати сучасні веб-переглядачі, або ви можете перекласти їх у разі потреби.

Підтримка браузера - 95% грудня 2019 року


14
Цикл for не працюватиме, оскільки parent.children / parent.childNodes є прямими списками; виклик видалення змінює список, тому ваш ітератор не гарантує повторення всього. У хромі, наприклад, ви пропускаєте будь-який інший вузол. Краще використовувати 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
qix

3
Класна стенограма, хоча і менш читабельна:while (parent.firstChild && !parent.firstChild.remove());
Гібольт

1
[...parent.childNodes].forEach(el => el.remove());

1
Це не працює в Typescript ... натомість використовуйтеwhile (selectElem.firstElementChild) { selectElem.firstElementChild.remove(); }
Джон Генкель

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's .empty (): усуває дані () всередині jquery, пов'язані з дочірніми тегами, використовуючи рекурсивний цикл (повільний, але може значно зменшити використання пам'яті), запобігає витоку пам'яті, якщо ви додаєте події без jquery, як-от використання .onclick = .... Якщо у вас немає подібних подій у дітей, які слід видалити, то встановлення InternalHTML не просочиться. Тож найкраща відповідь - це залежить.
Кріс Москіні

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

Дякуємо Андрію Лушникову за його посилання на jsperf.com (класний сайт!).

EDIT: щоб бути зрозумілим, різниця в продуктивності в Chrome між firstChild та lastChild не відрізняється. У верхній відповіді показано гарне рішення для продуктивності.


Те саме без попередження LINT: clear: function (контейнер) {for (;;) {var child = container.lastChild; якщо (! дитина) перерва; container.removeChild (дитина); }}
cskwg

9

Якщо ви хочете лише мати вузол без його дітей, ви також можете зробити його копію так:

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

Залежить від того, що ви намагаєтеся досягти.


3
Може, ти навіть міг би прослідкувати за цим parent.replaceChild(cloned, original)? Це може бути швидше, ніж видаляти дітей один за одним, і він повинен працювати над усім, що підтримує DOM (тому кожен тип XML-документа, включаючи SVG). Встановлення InternalHTML також може бути швидшим, ніж видалення дітей по одному, але це не працює на нічого, крім HTML-документів. Слід перевірити, що якийсь час.
Маартен

13
Це не скопіює доданих слухачами подій addEventListener.
Матвій

Якщо ви можете жити з цим обмеженням, це, безумовно, швидко: jsperf.com/innerhtml-vs-removechild/137
DanMan

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

6
element.textContent = '';

Це як внутрішній текст, крім стандартного. Це трохи повільніше , ніж removeChild(), але простіше у використанні і не буде робити велику частину різниці в продуктивності , якщо ви не дуже багато речей , щоб видалити.


Текстовий вузол не є елементом
check_ca

Вибачте, що ви праві, він видаляє дійсно всі елементи дітей
check_ca

У старих версіях Internet Explorer це не працюватиме.
pwdst

4

У відповідь Данман, Маартен і Метт. Клонування вузла, встановлення тексту - це справді життєздатний спосіб моїх результатів.

// @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. Крім того, якщо вам потрібно бути безпечним, вузол порожній перед додаванням вмісту, це справді корисно. Розглянемо випадок використання нижче.

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

Мені подобається це рішення! Будь-яка причина, яку я повинен використовувати це для встановлення InternalHTML в порожній рядок?
TheCodenator

продуктивність: domEl.innerHTML = ""погана та вважається поганою практикою
Адо Рен


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

2

innerText - переможець! http://jsperf.com/innerhtml-vs-removechild/133 . На всіх попередніх тестах внутрішній dom батьківського вузла був видалений спочатку ітерацією, а потім innerHTML або deleteChild, де застосовано до порожнього діла.


5
innerTextє власною річчю MS. Просто кажу.
DanMan

1
Досить впевнений, що це хибний тест - він покладається на boxте, що він доступний не як var, а через пошук DOM на основі id, і оскільки whileцикл робить цей повільний виклик багато разів його штрафують.
nrabinowitz

2

Найпростіший спосіб видалення дочірніх вузлів вузла за допомогою Javascript

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 Addon отримав відхилення, тому що я використовував innerHTML. Це була звичка давно. то я чотири. і я фактично помітив різницю швидкостей. при завантаженні Internalhtml пройшло деякий час, щоб оновити, однак відбувається додаток його миттєво!


1
добре, я думаю, швидше це чи ні, деякі тести на jsperf можуть довести будь-який випадок
Нікос М.

супер робота спасибі за ці тести. вони не включають for (var i=0...один, хоча. Але ось що я отримав, коли я запустив ці тести: i.imgur.com/Mn61AmI.png, тож у цих трьох тестах firstNode був швидшим, ніж lastNode та innerHTML, що справді круто
Noitidart

я додав тести: jsperf.com/innerhtml-vs-removechild/220 цікавих речей, які роблять метод el.firstNode - це найшвидший спосіб, як здається
Noitidart

2

Використання циклу діапазону вважається найбільш природним для мене:

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

Згідно з моїми вимірами в Chrome і Firefox, це приблизно на 1,3 рази повільніше. У звичайних умовах це, мабуть, не має значення.



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();     
}
  • Вибір батьківського діла
  • Використання методу "видалити" всередині циклу "Хоча" для усунення елемента "Перший дочірній", поки не залишиться жодного.

Поясніть свої кодові рядки, щоб інші користувачі могли зрозуміти його функціональність. Дякую!
Ігнасіо Ара

@IgnacioAra Готово!
Нееланш Верма

1

Щойно бачив, як хтось згадував це питання в іншому, і думав, що я додам метод, якого я ще не бачив:

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

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

Чітка функція - це взяти елемент і використовувати батьківський вузол, щоб замінити себе копією, не маючи дітей. Незначний приріст продуктивності, якщо елемент є рідким, але коли елемент має купу вузлів, підвищення продуктивності реалізується.


1
Насправді, здається, тут згадувалося stackoverflow.com/a/15441725/576767
Фелікс Ганьон-Греньє

Фелікс Я, мабуть, пропустив це, дякую за виправлення +1.
Гаррі Чилінгер’ян

1

Тільки функціональний підхід:

const domChildren = (el) => Array.from(el.childNodes)
const domRemove = (el) => el.parentNode.removeChild(el)
const domEmpty = (el) => domChildren(el).map(domRemove)

"childNodes" в domChildren видасть nodeList безпосередніх дочірніх елементів, який порожній, коли жоден не знайдений. Для того, щоб зіставити nodeList, domChildren перетворює його в масив. domEmpty відображає функцію domRemove над усіма елементами, які її видаляє.

Приклад використання:

domEmpty(document.body)

виводить усіх дітей з елемента тіла.


0

Інші способи в jQuery

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

Відхилення від голосування, тому що ОП заявив, що відповіді в прямому домені є кращими.
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. Отже, це насправді не знімає дітей, я б сказав.
Ар'ян

-1

просто і швидко використовувати для циклу !!

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

Якщо ви хочете щось повернути в це 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методом немає.


-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 ="";

})

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.