Reputation: 89
Using pure javascript, I'm trying to convert a string to NodeList and to append each NodeListItem to a container element, useinf forEach.
The string list has 6 items, but only 3 are appended. Why?
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);
});
Look at my experiment on JSFiddle: https://jsfiddle.net/lorenzodetomasi/kjhac52y/
Thank you.
Upvotes: 2
Views: 1856
Reputation: 1724
Not really sure why forEach
does not work on NodeList
, but you can iterae over all elements when you convert NodeList
to Array.from(controlNodes)
or Array.prototype.forEach.call(controlsNodes, function (controlNode) {...})
.
Please see corrected code below. For more info any examples refer to 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>
Upvotes: 0
Reputation: 106375
The key problem is using forEach
on result of childNodes
directly. Quoting the docs:
Node.childNodes
read-only property returns a liveNodeList
of child nodes of the given element [...]
In other words, each time you append a node from controlsNodes
, you move it from one place in DOM to another, and the corresponding NodeList is rebuilt - but forEach()
just doesn't care and moves forward to the next index of that NodeList. That's why each even element of the original collection is skipped.
There're several workarounds for that. The most straightforward is to convert dynamic NodeList to static Array - with either Array.from() or spread operator:
[...controlsNodes].forEach(function(controlNode, controls_index, listObj){
console.log(controlNode);
container.appendChild(controlNode);
});
An alternative approach is biting the bullet - taking into account the dynamic nature of NodeList:
while (controlsNodes.length !== 0) {
container.appendChild(controlsNodes[0]);
}
Upvotes: 4