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


876

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

Ответы:


1675

Вариант 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 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 только добавил поддержку 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 удаляет другие конструкции, такие как данные и обработчики событий, из дочерних элементов перед удалением самих элементов».
— отмечает

104
Кстати, использование lastChild кажется более эффективным jsperf.com/innerhtml-vs-removechild/15
— Андрей Лушников,

24
innerHTML работает только если вы имеете дело только с HTML. Если есть, например, SVG внутри, будет работать только удаление элемента
— stwissel

34
НИКОГДА, НИКОГДА, НИКОГДА не используйте innerHTML = ''. Не. Проблема в том, что элементы выглядят так, как будто они удалены, но внутренне узлы сохраняются и замедляют работу. Вы должны удалить все отдельные узлы по причине. Протестировано в Google Chrome и IE. Пожалуйста, рассмотрите возможность удаления «решения» innerHTML, так как это неправильно.
— Кендзи

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 (все еще медленнее, чем предыдущие два) - это удаление 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- дом-узел-в-JavaScript (новый URL для перезагрузки jsperf, потому что редактирование старого URL не работает)

«Цикл за тест» в Jsperf часто понимается как «за итерацию», и только у первой итерации есть узлы для удаления, поэтому результаты не имеют смысла, на момент публикации в этом потоке были неправильно настроены тесты.


2
Ваш jsperf полностью сломан. Это не очень хорошее доказательство.
— Брай

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

6
«InnerHTML не разрушит ваши обработчики событий и не испортит какие-либо ссылки jquery». Он потеряет хранящиеся данные jQuery.cache, что приведет к утечке памяти, поскольку единственная доступная ссылка для управления этими данными была на элементах, которые вы только что уничтожили.

1
«InnerHTML не разрушит ваши обработчики событий и не испортит любые ссылки jquery» относится к контейнеру, а не к дочерним элементам. cloneNodeотключает контейнер, что означает, что ссылки на контейнер не поддерживаются (jquery или иным образом). Кэш Jquery может позволить использовать jquery для удаления элементов, если к ним прикреплены данные jquery. Надеюсь, они переключатся на WeakMaps в какой-то момент. Очистка (или даже существование) $ .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 являются живыми списками; вызов remove изменяет список, и поэтому ваш итератор не гарантирует итерации по всему. Например, в Chrome вы пропускаете все остальные узлы. Лучше использовать 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 .empty (): устраняет data () внутри jquery, связанного с дочерними тегами, используя рекурсивный цикл (медленно, но может значительно уменьшить использование памяти), предотвращает утечки памяти, если вы вложили события без JQuery, как использовать .onclick = .... Если у вас нет таких событий в дочерних элементах, которые нужно удалить, то установка innerHTML не будет протекать. Так что лучший ответ - это зависит.
— Крис Москини

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 (классный сайт!).

РЕДАКТИРОВАТЬ: чтобы быть ясным, нет никакой разницы в производительности в Chrome между firstChild и lastChild. Верхний ответ показывает хорошее решение для производительности.


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

9

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

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

Зависит от того, что вы пытаетесь достичь.


3
Может быть, вы могли бы даже следовать с этим parent.replaceChild(cloned, original)? Это может быть быстрее, чем удаление дочерних элементов по одному, и должно работать на всем, что поддерживает DOM (то есть на всех типах XML-документов, включая SVG). Установка innerHTML также может быть быстрее, чем удаление дочерних элементов один за другим, но это не работает ни с чем, кроме документов 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 = '';

Это как innerText, кроме стандартного. Это немного медленнее, чем removeChild(), но проще в использовании и не будет иметь большого значения для производительности, если у вас нет слишком много материала для удаления.


Текстовый узел не является элементом
— check_ca

извините, вы правы, он действительно удаляет все дочерние элементы
— check_ca

Это не будет работать в старых версиях Internet Explorer.
— pwdst

4

В ответ на DanMan, Мартен и Мэтт. Клонирование узла для установки текста действительно является жизнеспособным способом в моих результатах.

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

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

Мне нравится это решение! По какой-то причине я должен использовать это вместо установки innerHTML в пустую строку?
— TheCodenator

производительность: domEl.innerHTML = ""плохая и считается плохой практикой
— Ado Ren


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

Это удалит все узлы в элементе.


... потому что это ненужный комплекс, и не дается объяснение того, как это работает (что на самом деле неочевидно), я подозреваю.
— Периата Breatta

2

innerText - победитель! http://jsperf.com/innerhtml-vs-removechild/133 . Во всех предыдущих тестах внутренний dom родительского узла удалялся на первой итерации, а затем innerHTML или removeChild, где применялся к пустому div.


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 был отклонен, потому что я использовал innerHTML. Это стало привычкой в ​​течение длительного времени. тогда я осуждаю это. и я на самом деле заметил разницу в скорости. при загрузке innerhtml потребовалось некоторое время, чтобы обновиться, однако, благодаря addElement, его моментально!


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 раза медленнее. В обычных условиях это, возможно, не будет иметь значения.


2
 let el = document.querySelector('#el');
 if (el.hasChildNodes()) {
      el.childNodes.forEach(child => el.removeChild(child));
 }

1
Пожалуйста, объясните в ответ, что делает ваш код?
— Нирав Джоши

1
Его само за себя
— Blacksheep

1

Как правило, JavaScript использует массивы для ссылки на списки узлов DOM. Таким образом, это будет хорошо работать, если вы заинтересованы в этом через массив HTMLElements. Кроме того, стоит отметить, потому что я использую ссылку на массив вместо прототипа JavaScript, это должно работать в любом браузере, включая IE.

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

1

Почему мы не следуем простейшему методу «удалить», зацикленному внутри while.

const foo = document.querySelector(".foo");
while (foo.firstChild) {
  foo.firstChild.remove();     
}
  • Выбор родительского div
  • Использование метода «remove» внутри цикла While для удаления первого дочернего элемента, пока не останется ни одного.

Пожалуйста, объясните ваши строки кода, чтобы другие пользователи могли понять его функциональность. Спасибо!
— Игнасио Ара

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

1

Просто видел, как кто-то упомянул этот вопрос в другом, и подумал, что я добавлю метод, которого я еще не видел:

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

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

Функция clear берет элемент и использует родительский узел для замены себя копией без дочерних элементов. Небольшой выигрыш в производительности, если элемент редкий, но когда элемент имеет несколько узлов, выигрыш в производительности реализуется.


1
На самом деле кажется, что это было упомянуто здесь stackoverflow.com/a/15441725/576767
— Феликс Ганьон-Гренье

Феликс Я, должно быть, пропустил это, спасибо за исправление +1.
— Гарри Chilinguerian

1

Функциональный подход:

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

«childNodes» в domChildren выдаст нодлист из ближайших дочерних элементов, который пуст, когда ничего не найдено. Чтобы отобразить нодлист, domChildren преобразует его в массив. domEmpty отображает функцию domRemove на все элементы, которая удаляет ее.

Пример использования:

domEmpty(document.body)

удаляет всех детей из элемента тела.


0

Другие способы в jQuery

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

Голосование с понижением, потому что OP сказал, что ответы в прямой DOM предпочтительнее.
— 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>тег
— Мохит Каркар

-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или, функция не работает впоследствии, но нет проблем с методом..lastChilddisplayHTML().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.