forEach не имеет доступа ко всем элементам в NodeList

Поскольку метод массива forEach теперь можно вызывать для массива NodeList. Мне было интересно, почему, если вы вносите изменения в содержимое childNodes NodeList во время цикла с forEach, forEach не обнаруживает изменений.

grid.childNodes.forEach(function(tableRow, index, nodeList) {
  tableRow.remove();
});

Здесь я пытаюсь перебрать дочерние элементы таблицы (сетки) и удалить каждого дочернего элемента (tableRow) из таблицы. Тем не менее, forEach никогда полностью не перебирает каждый элемент, всегда пропуская один последний tableRow, чтобы быть removed. Я думаю, что это как-то связано с живым NodeList childNodes, и forEach не может должным образом следовать за ним или что-то в этом роде.


person Dami    schedule 07.07.2018    source источник
comment
Какой у вас релевантный HTML? Какой селектор для grid?   -  person David says reinstate Monica    schedule 08.07.2018
comment
@ДэвидТомас const grid = document.querySelector("table");   -  person Dami    schedule 08.07.2018
comment
Вы знаете, что можете просто сделать grid.innerHTML = "", верно?   -  person trincot    schedule 08.07.2018
comment
Рискну предположить, что часть проблемы заключается в том, что количество дочерних элементов parentNode.children не равно количеству parentNode.childNodes.   -  person David says reinstate Monica    schedule 08.07.2018
comment
Возможный дубликат Как удалить элемент в список, используя forEach?   -  person Heretic Monkey    schedule 08.07.2018
comment
@HereticMonkey Это похоже, но NodeList, а не обычный List имеет значение для меня. Ответы в посте действительно помогают, спасибо.   -  person Dami    schedule 08.07.2018


Ответы (1)


Это связано с тем, что на более низком уровне .forEach() все еще ведет счет и использует индекс элементов. Когда вы удаляете некоторые из них, вы удаляете их из начала списка массивов/узлов, а затем индексатор становится неточным, потому что все индексы элементов должны сдвигаться вниз на единицу. Например, элемент, который раньше находился в позиции индекса 5, теперь находится в позиции индекса 4.

Для чего-то подобного всегда лучше использовать цикл подсчета и удалять элементы, начиная с последнего индекса и работая в обратном порядке. Это будет поддерживать правильный подсчет на протяжении всего цикла, потому что сопоставление элемента с индексом не будет изменено.

Вот пример:

let grid = document.getElementById("myTable");
document.querySelector("input").addEventListener("click", function(){
  for(i = grid.childNodes.length-1; i > -1; i--) {
    grid.removeChild(grid.childNodes[i]);
  }
});
<table id="myTable">
 <tr>
   <td>Row 1</td>
 </tr>
 <tr>
   <td>Row 2</td>
 </tr>
 <tr>
   <td>Row 3</td>
 </tr>
 <tr>
   <td>Row 4</td>
 </tr>
 <tr>
   <td>Row 5</td>
 </tr> 
</table>

<input type="button" value="Delete Rows">

Теперь вы также можете сделать это немного проще, если будете использовать API таблиц DOM и .deleteRow()

let grid = document.getElementById("myTable");
document.querySelector("input").addEventListener("click", function(){
  for(i = grid.rows.length-1; i > -1; i--) {
    grid.deleteRow(i);
  }
});
<table id="myTable">
 <tr>
   <td>Row 1</td>
 </tr>
 <tr>
   <td>Row 2</td>
 </tr>
 <tr>
   <td>Row 3</td>
 </tr>
 <tr>
   <td>Row 4</td>
 </tr>
 <tr>
   <td>Row 5</td>
 </tr> 
</table>

<input type="button" value="Delete Rows">

И, конечно же, вы можете просто стереть все содержимое своей таблицы, очистив файл .innerHTML.

let grid = document.getElementById("myTable");
document.querySelector("input").addEventListener("click", function(){      
    grid.innerHTML = "";
});
<table id="myTable">
 <tr>
   <td>Row 1</td>
 </tr>
 <tr>
   <td>Row 2</td>
 </tr>
 <tr>
   <td>Row 3</td>
 </tr>
 <tr>
   <td>Row 4</td>
 </tr>
 <tr>
   <td>Row 5</td>
 </tr> 
</table>

<input type="button" value="Delete Rows">

person Scott Marcus    schedule 07.07.2018
comment
почему начиная с последнего индекса? - person Dami; 08.07.2018
comment
@ObayanjuDamilare Я объяснил это в ответе. При удалении с самого начала элемент, который раньше был индексом, скажем, 5, становится индексом 4 - сопоставление индекса с элементом сбрасывается. Но когда вы удаляете с конца, этого не произойдет. - person Scott Marcus; 08.07.2018
comment
@ObayanjuDamilare Вы по-прежнему можете использовать .forEach и .remove, используя Array.from(grid.childNodes) вместо grid.childNodes. Массивы не отслеживают, какие элементы все еще являются частью DOM, как это делают NodeLists и HTMLCollections. Возможно, стоит отметить, что .childNodes создает live NodeList (это означает, что он изменяется при изменении дочерних узлов), в отличие от других методов, которые возвращают NodeList. HTMLCollections — это всегда живые списки. - person Sebastian Simon; 08.07.2018
comment
@ScottMarcus Спасибо за ваше время. Я понял ваше объяснение, и теперь код работает нормально. - person Dami; 08.07.2018
comment
Готово - person Dami; 09.07.2018