Endless loop through additional DOM element
Without using the formula for using XXX
This question is not meant to find a practical solution through a framework. Answer using frame XXX, or is it that simple in frame XXX, or why not use this XXX frame ??? does not answer the question.
I have a feature designed to run after the page load performShim
. This function iterates over all DOM elements that are tags span
, checks to see if they have an className
of shim
, and if so, calls it shim
, passing it the reference of the matched element.
My goal was to add another span
containing one iframe
to the element that is being passed to shim
.
With the code I've written so far, I can add to the parent just fine. However, if I comment out the line append
and instead try the prefix line the browser is hanging in a supposedly infinite loop.
It is not easy for me to understand why this is so.
function shim( element ) {
var iframe = document.createElement('iframe');
iframe.setAttribute( 'frameborder', '0' );
iframe.setAttribute( 'scrolling', 'no' );
iframe.setAttribute( 'align', 'bottom' );
iframe.setAttribute( 'marginheight', '0' );
iframe.setAttribute( 'marginwidth', '0' );
iframe.setAttribute( 'src', "javascript:'';" );
var span = document.createElement('span');
span.appendChild(iframe);
//element.parentNode.insertBefore(span,element); //causes infinite loop?
element.parentNode.appendChild(span); //this line works OK
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
var originalDisplay = els.display;
els.visibility = 'hidden';
els.position = 'absolute';
els.display = 'inline';
var width = element.offsetWidth;
var height = element.offsetHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
iframe.style.width = (width-6) + 'px';
iframe.style.height = (height-6) + 'px';
}
function performShim() {
var children = document.getElementsByTagName("span");
for( var i = 0; i < children.length; i++ ) {
if( children[i].className == "shim" ) {
shim(children[i]);
}
}
}
source to share
The A NodeList
(for example, the returned one document.getElementsByTagName
) is usually a live list - changes made to the DOM are also reflected in it. So every time you add span
before the current, you expand the list with one element and move the current element by one, and the next iteration takes you back to the node you just finished.
You have some easy workarounds for this ...
-
Kill the counter when you add node.(Awful, and if you ever add something insteadspan
, you end up missing nodes and it won't be obvious why.) -
Copy list to array and iterate over the array. You can do it with something like
children = [].slice.call(children, 0);
(more often) orchildren = Array.apply(window, children);
. -
Use
document.querySelectorAll
one that returns you a NodeList, which won't work. (And even if it was live, in which case you could choose'span.shim'
, and the inserted gaps did not appear in this case.) -
Iterations backward (from
children.length - 1
to 0).
source to share