Удаление всех дочерних элементов узла DOM в JavaScript
Как мне удалить все дочерние элементы узла 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 ответ(ов)
Текущий принятый ответ неверен относительно того, что использование 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", и только в первой итерации есть узлы для удаления, поэтому результаты становятся бессмысленными. На момент публикации в этом треде были некорректно настроенные тесты.
Для итеративного удаления всех дочерних элементов node
из DOM можно использовать следующий однострочник:
Array.from(node.children).forEach(c => c.remove())
Или альтернативный вариант:
[...node.children].forEach(c => c.remove())
Оба способа создают массив из дочерних элементов node
и применяют метод forEach
, чтобы удалить каждого дочернего элемента с помощью метода remove()
.
Если вы используете jQuery, вы можете очистить содержимое элемента следующим образом:
$('#foo').empty();
Если же вы не используете jQuery, вы можете сделать то же самое с помощью чистого JavaScript:
var foo = document.getElementById('foo');
while (foo.firstChild) {
foo.removeChild(foo.firstChild);
}
Оба варианта приведут к тому, что все дочерние элементы элемента с идентификатором foo
будут удалены. Выбор между jQuery и чистым JavaScript зависит от вашего проекта и требований к производительности, но оба подхода эффективны.
Если вам нужно создать копию элемента без его дочерних элементов, вы можете сделать это следующим образом:
var dupNode = document.getElementById("foo").cloneNode(false);
Это зависит от того, что именно вы хотите достичь.
В ответ на ваш вопрос, я провел тест производительности для наиболее критичного сценария, который включает:
- Очистку содержимого
- Сохранение оригинального элемента
- Удаление большого количества дочерних элементов
Вот мой HTML-код:
<div id="target">
<div><p>Title</p></div>
<!-- всего 1000 -->
<div><p>Title</p></div>
</div>
Я протестировал несколько подходов для очистки содержимого родительского элемента:
Использование innerHTML:
target.innerHTML = "";
Удаление последнего дочернего элемента:
while (target.hasChildNodes()) { target.removeChild(target.lastChild); }
Удаление первого дочернего элемента:
while (target.hasChildNodes()) { target.removeChild(target.firstChild); }
Использование метода 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.
Получить координаты (X,Y) HTML-элемента
Как узнать, какой элемент DOM имеет фокус?
Как определить, виден ли элемент DOM в текущей области просмотра?
Как выбрать элемент по имени с помощью jQuery?
Создание нового DOM-элемента из HTML-строки с использованием встроенных методов DOM или Prototype