I have a widget-like block of HTML+JavaScript that people can copy/paste into their HTML-page one or multiple times. That block checks if an external JavaScript file is already in the DOM, and loads it if not, something like this:
(function(){
d = document;
if (!d.getElementById('ex-scr')) {
scr = d.createElement('script');
scr.async = true;
scr.id = 'ex-scr';
scr.src = 'external.js';
d.getElementsByTagName('head')[0].appendChild(scr)
}
})();
The external JavaScript-file checks the HTML-page for instances of the widget (using getElementsByClassName) and does stuff with those instances, kind of like this;
for (var i=0;i<document.getElementsByClassName('target').length;i++) {
document.getElementsByClassName('target')[i].style.borderStyle="solid";
}
A working example of this can be found on http://futtta.be/opera_enigma.html.
This works perfectly in Firefox (3.6 & 4b), Chrome (5 & 6) and Safari, but does not work as expected in Opera (tested with most recent version, 10.61): no matter how many 'widgets' (divs with class='target'
) are present, Opera only acts on the first one because apparently the nodeList only contains 1 entry (length is 1 instead of 2 or 3 or ...).
The problem goes away if in the widget's javascript I call the function to load the external script with window.onload, but I'd like my widget to activate as soon as possible (without interfering with the rest of the page, hence the asynchronous stuff).
So my questions; is there a bug in my code which Firefox, Safari and Chrome ignore? Is this a bug in Opera? How can I get Opera to behave?
var d = document
! Less global namespace pollution, please! :) Also, remember that every time you accessdocument.getElementsByClassName("target")
it has to regenerate that list. So you should do something likefor (var i = 0, a = Array.prototype.slice.call(document.getElementsByClassName("target")), len = a.length; i < len; i++) { a[i].style.borderStyle = "solid"; }
– Sob