14

Удаление всех дочерних элементов узла DOM в JavaScript

14

Как мне удалить все дочерние элементы узла 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.

5 ответ(ов)

1

Текущий принятый ответ неверен относительно того, что использование innerHTML медленнее (по крайней мере в IE и Chrome), как правильно заметил m93a.

Chrome и Firefox работают значительно быстрее при использовании следующего метода (хотя он уничтожит привязанные данные jQuery):

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

На втором месте по скорости среди Firefox и 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, потому что редактирование старой ссылки не работает)

Важно отметить, что "per-test-loop" на JsPerf часто понимается как "per-iteration", и только в первой итерации есть узлы для удаления, поэтому результаты становятся бессмысленными. На момент публикации в этом треде были некорректно настроенные тесты.

0

Для итеративного удаления всех дочерних элементов node из DOM можно использовать следующий однострочник:

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

Или альтернативный вариант:

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

Оба способа создают массив из дочерних элементов node и применяют метод forEach, чтобы удалить каждого дочернего элемента с помощью метода remove().

0

Если вы используете jQuery, вы можете очистить содержимое элемента следующим образом:

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

Если же вы не используете jQuery, вы можете сделать то же самое с помощью чистого JavaScript:

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

Оба варианта приведут к тому, что все дочерние элементы элемента с идентификатором foo будут удалены. Выбор между jQuery и чистым JavaScript зависит от вашего проекта и требований к производительности, но оба подхода эффективны.

0

Если вам нужно создать копию элемента без его дочерних элементов, вы можете сделать это следующим образом:

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

Это зависит от того, что именно вы хотите достичь.

0

В ответ на ваш вопрос, я провел тест производительности для наиболее критичного сценария, который включает:

  • Очистку содержимого
  • Сохранение оригинального элемента
  • Удаление большого количества дочерних элементов

Вот мой HTML-код:

<div id="target">
  <div><p>Title</p></div>
  <!-- всего 1000 -->
  <div><p>Title</p></div>
</div>

Я протестировал несколько подходов для очистки содержимого родительского элемента:

  1. Использование innerHTML:

    target.innerHTML = "";
    
  2. Удаление последнего дочернего элемента:

    while (target.hasChildNodes()) {
        target.removeChild(target.lastChild);
    }
    
  3. Удаление первого дочернего элемента:

    while (target.hasChildNodes()) {
        target.removeChild(target.firstChild);
    }
    
  4. Использование метода replaceChildren:

    target.replaceChildren();
    
Результаты:
innerHTML: 1,222,273 ops/sec ±0.47% (63 runs sampled)
lastChild: 1,336,734 ops/sec ±0.87% (65 runs sampled)
firstChild: 1,313,521 ops/sec ±0.74% (64 runs sampled)
replaceChildren: 743,076 ops/sec ±1.08% (53 runs sampled)

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

innerHTML: 1,191,838 ops/sec ±0.73% (62 runs sampled)
lastChild: 1,352,657 ops/sec ±1.42% (63 runs sampled)
firstChild: 1,327,664 ops/sec ±1.27% (65 runs sampled)
replaceChildren: 754,166 ops/sec ±1.88% (61 runs sampled)
Выводы:

Современное API оказалось медленнее. Способы с удалением первого и последнего дочернего элемента показывают близкие результаты, но могут быть небольшие отклонения в зависимости от конкретного теста. Приведу данные для сравнений по предыдущим тестам:

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

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

firstChild: 1,372,109 ops/sec ±0.37% (66 runs sampled)
lastChild: 1,355,811 ops/sec ±0.35% (65 runs sampled)
lastChild: 1,364,830 ops/sec ±0.65% (64 runs sampled)
firstChild: 1,365,617 ops/sec ±0.41% (65 runs sampled)

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

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

P.S. Тестирование проводилось в браузере Firefox 111.0.1.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь