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