Opera, REST APIs, Module Patterns and generated script nodes
I just came across an annoying thing in Opera. I love using the Module Pattern for designing my JavaScripts and I love generating script nodes on the fly for REST APIs that give me back JSON. The following example using the twitter API works swimmingly on Firefox and Safari (have to check IE later, but I’d be surprised if it didn’t):
<script type="text/javascript">
twitterbadge = function(){
function show(result){
alert(result);
};
var restURL = 'http://twitter.com/statuses/user_timeline/' +
'codepo8.json?callback=twitterbadge.show' +
'&count=10';
var s = document.createElement('script');
s.setAttribute('src',restURL);
document.getElementsByTagName('head')[0].appendChild(s);
return {
show:show
};
}();
</script>
However, if you test this in Opera you get an error:
JavaScript - file://localhost/Applications/MAMP/htdocs/operacallbackfail.html
Inline script thread
Error:
name: ReferenceError
message: Statement on line 1: Reference to undefined variable: twitterbadge
Backtrace:
Line 1 of linked script http://twitter.com/statuses/user_timeline/codepo8.json?callback=twitterbadge.show&count=10
twitterbadge.show([{user : {screen_name : "codepo8",...08"}]);
It seems like the newly generated script node calls the method of the module before the module has been created. In other words, newly generated script nodes halt the execution of the code that generated them. The following example works across browsers, including Opera:
<script type="text/javascript">
twitterbadgeOperaSafe = function(){
function show(result){
alert(result);
};
var restURL = 'http://twitter.com/statuses/user_timeline/' +
'codepo8.json?callback=twitterbadgeOperaSafe.show' +
'&count=10';
var s = document.createElement('script');
s.setAttribute('src',restURL);
return {
show:show,
init:function(){
document.getElementsByTagName('head')[0].appendChild(s);
}
};
}();
twitterbadgeOperaSafe.init();
</script>
Not too daunting a fix, but it feels wrong to have to have an extra method and another line calling it.
Try it out for yourself: Opera bug with dynamic script nodes and module pattern
Technorati Tags: Opera, REST, JSON, bug, dynamic, javascript, scriptnodes
April 11th, 2008 at 2:56 pm
I am not sure we can qualify it as a "bug". I don't think the spec require the browser to wait the current execution before execute the new script.
The "ideal" behaviour will be to execute the new script and the current one in parallel and you risk a race condition problem.
Your first script makes an assumption about what is executed when. It appears that this assumption is true in some browsers implementations but it not required to be true per the spec.
April 11th, 2008 at 5:46 pm
What about deferring script insertion?
setTimeout(function(){
document.getElementsByTagName('head')[0].appendChild(s)
}, 10);
- kangax
April 11th, 2008 at 6:35 pm
Kangax is right, deferring the insert works. This would simulate what is happening in the other browsers. You would only need a millisecond, that's enough to push it to the next round.
Quick question: If Opera is executing the script right there in mid-function, why isn't it scoped by that function? This seems really wrong or inconsistent with load time behavior, why isn't then the second included script on page then evaluated mid execution of first script, when it arrives?
It would be preferable if it were safe to run an include in mid-function. I agree that this isn't really a bug, as such, but rather really unfortunate behavior. I don't know about you guys, but I usually "assume" that my lines of code are run in sequence. :-)
April 11th, 2008 at 7:22 pm
I agree, it seems like you're trying to use the callback before it may be defined. I think your twitterbadgeOperaSafe code is just fine as is, and doesn't need to be labeled as OperaSafe at all.
Just a question though, why no var declaration?
April 13th, 2008 at 7:08 pm
The original code works in the latest 9.50 beta snapshot, as does the OperaSafe code.
April 29th, 2008 at 5:21 am
Well, I found the answer to my own question eventually.
In the interest of anyone also wondering:
No var declaration for globals
http://yuiblog.com/blog/2008/04/16/global-domination-part-two/
Although, I personally don't agree with it, but thats at least why some Yahoo's don't use it.