Чистый javascript: преобразовать строку в nodelist

Используя чистый javascript, я пытаюсь преобразовать строку в NodeList и добавить каждый NodeListItem к элементу контейнера, useinf forEach.

Список строк состоит из 6 элементов, но добавляются только 3. Почему?

var controlsString = '<button class="{{namespace}}_prev" onclick="isotype_sliders[{{index}}].slide_to(\'prev\');">Slide to prev</button>'+
                '<button class="{{namespace}}_pause" onclick="isotype_sliders[{{index}}].pause(); this.style.display=\'none\'; this.nextElementSibling.style.display=\'initial\';">Pause</button>'+
                '<button class="{{namespace}}_play" style="display: none;" onclick="isotype_sliders[{{index}}].play(); this.style.display=\'none\'; this.previousElementSibling.style.display = \'initial\';">Play</button>'+
                '<button class="{{namespace}}_next" onclick="isotype_sliders[{{index}}].slide_to(\'next\');">Slide to next</button>'+
                '<button class="{{namespace}}_shuffle" onclick="isotype_sliders[{{index}}].shuffle();">Shuffle</button>'+
                '<button class="{{namespace}}_slide_to" onclick="isotype_sliders[{{index}}].slide_to(2);">Slide to 2</button>';
var container = document.getElementById('container');
var getNodes = str => new DOMParser().parseFromString(str, 'text/html').body.childNodes;
var controlsNodes = getNodes(controlsString);
console.log(controlsNodes);
controlsNodes.forEach(function(controlNode, controls_index, listObj){
    console.log(controlNode);
    container.appendChild(controlNode);
});

Посмотрите мой эксперимент с JSFiddle: https://jsfiddle.net/lorenzodetomasi/kjhac52y/

Спасибо.

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

degr 11.04.2018 13:13
2
1
863
2

Ответы 2

Ключевая проблема заключается в использовании forEach непосредственно на результате childNodes. Цитируя документы:

Node.childNodes read-only property returns a liveNodeList of child nodes of the given element [...]

Другими словами, каждый раз, когда вы добавляете узел из controlsNodes, вы перемещаете его из одного места в DOM в другое, и соответствующий NodeList перестраивается, но forEach() просто не заботится и переходит к следующему индексу этого NodeList. Вот почему каждый четный элемент исходной коллекции пропускается.

Для этого есть несколько обходных путей. Самый простой - преобразовать динамический NodeList в статический массив - с помощью Array.from () или оператор распространения:

[...controlsNodes].forEach(function(controlNode, controls_index, listObj){
    console.log(controlNode);
    container.appendChild(controlNode);
});

Альтернативный подход - кусочек пули, учитывая динамический характер NodeList:

while (controlsNodes.length !== 0) {
  container.appendChild(controlsNodes[0]);
}

Не совсем уверен, почему forEach не работает на NodeList, но вы можете перебирать все элементы при преобразовании NodeList в Array.from(controlNodes) или Array.prototype.forEach.call(controlsNodes, function (controlNode) {...}).

См. Исправленный код ниже. Для получения дополнительной информации любые примеры обращайтесь к https://developer.mozilla.org/en-US/docs/Web/API/NodeList.

var controlsString = '<button class="{{namespace}}_prev" onclick="isotype_sliders[{{index}}].slide_to(\'prev\');">Slide to prev</button>'+
                '<button class="{{namespace}}_pause" onclick="isotype_sliders[{{index}}].pause(); this.style.display=\'none\'; this.nextElementSibling.style.display=\'initial\';">Pause</button>'+
                '<button class="{{namespace}}_play" style="display: none;" onclick="isotype_sliders[{{index}}].play(); this.style.display=\'none\'; this.previousElementSibling.style.display = \'initial\';">Play</button>'+
                '<button class="{{namespace}}_next" onclick="isotype_sliders[{{index}}].slide_to(\'next\');">Slide to next</button>'+
                '<button class="{{namespace}}_shuffle" onclick="isotype_sliders[{{index}}].shuffle();">Shuffle</button>'+
                '<button class="{{namespace}}_slide_to" onclick="isotype_sliders[{{index}}].slide_to(2);">Slide to 2</button>';
var container = document.getElementById('container');
var getNodes = str => new DOMParser().parseFromString(str, 'text/html').body.childNodes;
var controlsNodes = getNodes(controlsString);

Array.from(controlsNodes).forEach(function(controlNode, controls_index, listObj){
    console.log(controlNode);
    container.appendChild(controlNode);
});
<div id='container'></div>

Другие вопросы по теме