querySelectorAll не работает

у меня есть требование, где я должен забрать последний .div внутри контейнера и примените к нему некоторую бизнес-логику. Выбор последнего .div должен быть динамическим, потому что у пользователя есть возможность добавить / удалить .div элементы.

Первоначально я пытался с querySelectorAll но, похоже, это не сработало. Поэтому я решил изменить его на getElementsByClassName и удивительно, что он работал с той же логикой. Может кто-нибудь, пожалуйста, помогите мне с причиной того, почему remove_div не работает пока второй (remove_div_2), нет?

примечание: Я не ищу исправления/решения проблемы, потому что я уже приступил ко второму варианту. Я просто хочу знать причину, почему опция с querySelectorAll не работает.

ниже мой код:

HTML-код:

<div id='container'>
    <div id='div1' class='div'>This is Div 1</div>
    <div id='div2' class='div'>This is Div 2</div>
    <div id='div3' class='div'>This is Div 3</div>
</div>
<button type='button' id='append_div'>Append Div</button>
<button type='button' id='remove_div'>Remove Div</button>
<button type='button' id='remove_div_2'>Remove Div 2</button>

JavaScript:

window.onload = function () {
    var elementToStyle = document.querySelectorAll("#container .div");
    elementToStyle[elementToStyle.length - 1].classList.add('red');

    document.getElementById('append_div').onclick = function () {
       var divToInsert = document.createElement('div');
       divToInsert.id = 'new_div';
       divToInsert.className = 'div';
       divToInsert.innerHTML = 'This is an appended div';
       document.getElementById('container').appendChild(divToInsert);
       var elToStyle = document.querySelectorAll("#container .div");
       for (i = 0; i < elToStyle.length; i++)
           elToStyle[i].classList.remove('red');
           elToStyle[elToStyle.length - 1].classList.add('red');
       };

       document.getElementById('remove_div').onclick = function () {
           var elToStyle = document.querySelectorAll("#container .div");
           document.getElementById('container').removeChild(elToStyle[elToStyle.length - 1]);
           elToStyle[elToStyle.length - 1].classList.add('red');
       };

       document.getElementById('remove_div_2').onclick = function () {
           var elToStyle = document.getElementsByClassName('div');
           document.getElementById('container').removeChild(elToStyle[elToStyle.length - 1]);
           elToStyle[elToStyle.length - 1].classList.add('red');
       };
   }

2 ответов


причина в том, что querySelectorAll метод возвращает статический список. Любые изменения, внесенные в документ после querySelectorAll используется (например,removeChild в этом случае) не будет отражаться в списке возвращаемых узлов. Отсюда elToStyle[elToStyle.length - 1] будет указывать на узел, который был удален.

а, getElementsByClassName С другой стороны возвращает текущий список узлов. Это означает, что elToStyle[elToStyle.length - 1] всегда будет указывать на последний .div независимо от того, какие изменения были внесены в документ после узла список был подготовлен или нет.

ниже-выдержка из официальной документации здесь

объект NodeList, возвращаемый методом querySelectorAll() должен быть статический, не живой ([DOM-LEVEL-3-CORE], раздел 1.1.1). Последующий изменения в структуре базового документа не должны отражается в объекте NodeList. Это означает, что объект будет вместо этого содержит список совпадающих узлов элементов, которые были в этот документ на момент создания списка.

Примечание: вы можете увидеть это, сделав console.log(elToStyle); до и после removeChild.


Если вы хотите ссылаться на последний элемент деления, просто сделайте следующее...

var id = 'container';
var d = document.getElementById(id).getElementsByTagName('div')
var lastDiv = d[d.length - 1];

..а затем примените свой querySelector.