Удаление элементов HTMLCollection из DOM

у меня есть коллекция элементов параграфа. Некоторые из них пусты, а некоторые содержат только пробелы, в то время как другие имеют содержимое:

<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
<p></p>
<p> </p>
<p>    </p>
<p>&nbsp;</p>
<p> &nbsp;</p>

Я использую getElementsByTagName выбрать их:

var paragraphs = document.getElementsByTagName('p');

это возвращает все абзацы в документе. Я хочу удалить их все, поэтому я хотел бы работать

for (var i = 0, len = paragraphs.length; i < len; i++) {
   paragraphs[i].remove();
}

но я Uncaught TypeError: Cannot read property 'remove' of undefined ошибки. Я думаю, что это странно, но рисунок я попробую добавить охранника и посмотреть, что произойдет:

for (var i = 0, len = paragraphs.length; i < len; i++) {
   paragraphs[i] && paragraphs[i].remove();
}

нет ошибки, но не все элементы удаляются. Поэтому я запускаю его снова, и он удаляет некоторые элементов, которые не были удалены ранее. Я управляю им снова и наконец-то из документа удаляются все абзацы.

мне интересно, какую очевидную деталь я упускаю здесь.

демонстрация проблемы

4 ответов


проблема в том, что paragraphs это список. Удалив p элемент, вы также меняете этот список. Простое исправление-перебирать список в обратный порядок:

for (var i = paragraphs.length - 1; i >= 0; --i) {
  paragraphs[i].remove();
}

альтернативным решением является создание статический список (не-жить список). Вы можете сделать это, либо:

  • преобразование списка в Array:

    var paragraphs =
      Array.prototype.slice.call(document.getElementsByTagName('p'), 0);
    
  • используя document.querySelectorAll:

    var paragraphs = document.querySelectorAll('p');
    

затем вы можете перебирать список в обычном порядке (используя for loop):

for (var i = 0; i < paragraphs.length; ++i) {
  paragraphs[i].remove();
}

или (с помощью for...of loop):

for (var paragraph of paragraphs) {
  paragraph.remove();
}

отметим, что .remove является относительно новым методом DOM и не поддерживается в каждом браузере. Вижу документация MDN дополнительные информация.


чтобы проиллюстрировать проблему, давайте представим, что у нас есть список узлов из трех элементов,paragraphs = [p0, p1, p2]. Тогда вот что происходит, когда вы перебираете список:

i = 0, length = 3, paragraphs[0] == p0  => paragraphs = [p1, p2]
i = 1, length = 2, paragraphs[1] == p2  => paragraphs = [p1]
i = 2, length = 1, END

Итак, в этом примере,p1 не удаляется, потому что он пропущен.


длина HTMLCollection изменяется при удалении элемента. Способ сделать это-использовать цикл while

while(paragraphs.length > 0) {
   paragraphs[0].remove();
}

вы должны использовать removeChildна нем родитель, чтобы сделать это:

for (var i = 0, len = paragraphs.length; i < len; i++) {
    paragraphs[i].parentNode.removeChild(paragraphs[i]);
}

EDIT: plnkr, похоже, не работает хорошо отсюда, поэтому я не тестировал, но он должен работать:

window.addEventListener("load", function() {
    var paragraphs = document.getElementsByTagName('p');

    var loop = function() {
        for (var i = 0, len = paragraphs.length; i < len; i++) {
            paragraphs[i].parentNode.removeChild(paragraphs[i]);
        }
    };

    console.log(paragraphs.length) // 7
    loop();
    console.log(paragraphs.length) // 3
    loop();
    console.log(paragraphs.length) // 1
    loop();
    console.log(paragraphs.length) // 0
});

в моем случае это было мое решение:

var temp = document.getElementsByClassName('level-size');
for (var i = 0, len = temp.length; i < len; i++)
    temp[0].remove();

temp[0] потому что каждый раз, когда я удаляю его, таблица отверстий отодвигается на один индекс.