I am trying to dynamically load a JavaScript file by adding a script element to the head element, after wh开发者_开发问答ich I test for the presence of a function defined inside that file to check that the load succeeded.
If I use this code:
var scriptElem = document.createElement("script");
scriptElem.type = "text/javascript";
scriptElem.src = 'myfile.js';
document.getElementsByTagName('head')[0].appendChild(scriptElem);
the new function isn't defined afterwards. However, if I alter the last line to use jQuery, like so:
$('head').append($(scriptElem));
it is. As another clue, in the first case Firebug shows the new script element in the HTML tab, whereas in the second case it doesn't.
I have tried using jQuery.getScript() to do this, but that didn't work either. Also, if it's relevant, the call is being made from a function supplied to jQuery via:
$(document).ready();
Can someone please explain what's going on?
It looks like jQuery might be giving you a bit of help. From my hacking, $('head').append(scriptElem); does append the script, but it doesn't parse it immediately. So if you put a function inside called "function something(){};" then it's defined on the following line using the JQuery append() method, but not using the native appendChild. (That's as you've described.) However, if you put setTimeout(function(){alert(something)},1); then it is defined. It looks as if JQuery is managing to append the script tag but also force the browser to parse it immediately.
The key seems to be in the domManip function of the JQuery source code which contains the line:
if ( scripts ) {
jQuery.each( scripts, evalScript );
}
which then goes to this:
function evalScript( i, elem ) {
if ( elem.src ) {
jQuery.ajax({
url: elem.src,
async: false,
dataType: "script"
});
} else {
jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
}
if ( elem.parentNode ) {
elem.parentNode.removeChild( elem );
}
}
So JQuery treats script tag appends as a special case, and some other JQuery magic goes and evaluates them synchronously, hence your function is available immediately.
From looking at the jQuery source code, when jQuery comes across a script element in .append()
, it runs it through a function called evalScript()
:
function evalScript( i, elem ) {
if ( elem.src ) {
jQuery.ajax({
url: elem.src,
async: false,
dataType: "script"
});
} else {
jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
}
if ( elem.parentNode ) {
elem.parentNode.removeChild( elem );
}
}
As you can see, jQuery makes a synchronous xhr to fetch the script and evaluate it programmatically instead. This is why the function is defined right after append()
has finished. With .appendChild()
, the script specified will be downloaded asynchronously and the currently executing script will finish before the newly added one is parsed.
Dynamically loading JavaScript by creating a script element is flawed. The request for the file is inherently asynchronous, and the file won't necessarily be downloaded, inserted, and evaluated before the browser moves on to another JavaScript element.
Many dynamic JavaScript loaders use some kind of callback function, which works by attaching an event handler to the <script>
element you're inserting.
The other alternative, which is the one used by jQuery and the JSAN JSModule, is an AJAX request. The code sets up a synchronous request to download all of the source code for the JavaScript file you're requesting. Once downloaded, it calls eval()
on the downloaded source code to execute it in the global namespace. This makes the browser wait until the code has been downloaded and executed before it continues.
Using this method, you can reliably assume that any calls to functions defined in a dynamically loaded source file will be available by the time the browser gets to the line of code requiring it.
You can use that!
$("myfile.js").getScript(function(){ /* do something */ });
big mistake in the previous response. Use that:
$.getScript("myfile.js", function(){ /* do something */ });
精彩评论