When using an importjs() type of function (see below for an example), jQuery doesn't seem to be loading before the code following it.
Here's a sample html file:
<html>
<head></head>
<body>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
function importjs(jsFile) {
var body = document.getElementsByTagName('head').item(0);
var scpt = document.createElement('script');
scpt.src = jsFile;
scpt.type = 'text/javascript';
body.appendChild(scpt);
}
var f1="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js";
//importjs(f1);
var $j=jQuery;
alert("hello stackoverflow!");
</script>
</body>
</html>
With the above code, the alert should successfully fire.
Next, comment out the first script block, i.e. the one explicitly loading jQuery, and uncomment the importjs(f1) line in the second script block. This time, the alert does not fire, at least in firefox and safari.
Now, put in an extra alert before the line "var $j=jQuery". For me, it works in both browsers, regardless of how long or short I wait. A setTimeout would probably also do the trick, but it's also not an ideal way to program something like this.
If javascript is single-threaded, why does the importjs fail? Is it because the new element created by importjs doesn't get 'executed' until the first block finishes, or should the new element be executed as soon as it is created?
There are several problems here:
you have jQuery duplicated, one in the html, one in the js
dynamically added javascript won't be available immediately
if you load scripts this way the dependant code should be in a callback function
function importjs(jsFile, callback) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.src = jsFile;
script.type = 'text/javascript';
script.onload = script.onreadystatechange = function() {
// execute callback
if (callback) callback();
// prevent memory leak in IE
script.onload = null;
head.removeChild(script);
};
head.appendChild(script);
}
then you should use it as:
importjs("jquery.js", function(){
// all jQuery dependant code
// goes here...
});​
UPDATE
There is a more robust solution for including javascript files which allows you to:
include multiple files that are related
ensure they are executed in order
load them in a non-blocking way (parallel with other resources)
I'm still working on this script, but pretty much works right now. Be sure to check it out.
It combines the advantages of different techniques to give a huge benefit on page load time. Here is a related article: Loading Scripts Without Blocking
The syntax is:
include(['jquery.js','jquery-ui.js'], myjQueryCode); // executed in order
Related
I'm try for delaying the execution of this ad script:
<script type="text/javascript">
var _pop = _pop || [];
_pop.push(['siteId', 809347]);
_pop.push(['minBid', 0.000000]);
_pop.push(['popundersPerIP', 0]);
_pop.push(['delayBetween', 0]);
_pop.push(['default', false]);
_pop.push(['defaultPerDay', 0]);
_pop.push(['topmostLayer', false]);
(function() {
var pa = document.createElement('script'); pa.type = 'text/javascript'; pa.async = true;
var s = document.getElementsByTagName('script')[0];
pa.src = '//URL/pop.js';
pa.onerror = function() {
var sa = document.createElement('script'); sa.type = 'text/javascript'; sa.async = true;
sa.src = '//URL/pop.js';
s.parentNode.insertBefore(sa, s);
};
s.parentNode.insertBefore(pa, s);
})();
</script>
For do this I have apply setTimeout in this way:
setTimeout (function() {
(function() {
var pa = document.createElement('script'); pa.type = 'text/javascript'; pa.async = true;
var s = document.getElementsByTagName('script')[0];
pa.src = '//c1.popads.net/pop.js';
pa.onerror = function() {
var sa = document.createElement('script'); sa.type = 'text/javascript'; sa.async = true;
sa.src = '//c2.popads.net/pop.js';
document.head.appendChild(sa, s);
};
document.head.appendChild(pa, s);
})(); }, 2300);
</script>
And changed s.parentNode.insertBefore with document.head.appendChild
The script start but I not see delay.
I have read "If the target script expects to be run synchronously and uses document.write you're out of luck. Unless, you want to do some messy hacks involving overwriting of the native document.write function.
I need for overwriting document.write?
You can delay the execution of any script by many methods. Simple ones:
Place your script tags just before the end of your page body, so that they are loaded after the browser has parsed the rest of the page. Therefore the browser is able to render the page before your script executes.
Add the defer="defer" attribute to your script tag (if it loads an external file). Execution is delayed at the end of page parsing.
Use a setTimeout wrapped around the code to be delayed, as you tried. Make sure the setTimeout itself is wrapped to be executed on DOM ready event, otherwise it will start the counter at execution, before the rest of the page is rendered, and you may see no effect if that rendering is slow.
Load a script asynchronously after a given timeout (but refer to point 3 about DOM ready event). In your case, why not putting the ad loader code in an external file (so you do not even have to modify it!), and load it later (using a similar script loading method)? Yes, that would be loading a loader…
Side notes:
Use of someScriptElement.parentNode.insertBefore is considered a better practice than document.head.appendChild, as a document may not have a head tag.
The sentence you quoted means that any script that relies on document.write forces you to load it synchronously (and therefore document.write should be avoided whenever possible). You may try to emulate the synchronous load by overwriting the document.write function, so that you could nevertheless load the script asynchronously. But in your case, the ad code does not use document.write, and it loads another script asynchronously, so you do not have to bother.
Let's say I've got my-file.js or some CDN file in different server that has
for (var i = 0; i < 1000; i ++) {
//something really long and hard to execute
}
//after this long execution
window.myObj = {
//initialization of some global object that I need
}
(I cannot change my-file.js...)
I want to add my-file.js asynchronously to page, and then, after it is loaded and EXECUTED I want to call some event like:
//when my my-file.js is loaded I finally use window.myObj
window.myObj.somefunc(); //yaaay
Is it possible? (cross browser for IE9+ is important for me, but any not critical)
Note:
In case file I need is on CDN or somewhere on different server - I need to remember about cross-domain policy, so I think loading content of file in ajax is not possible in such case.
Note 2:
http://www.chromestatus.com/features/5711871906676736 there is exacly what I need, but I bet it'll be few years before you can easly use it globally.
var script = document.createElement('script');
script.onload = function(){
// loaded
}
document.head.appendChild(script);
script.src = "path/to/script";
That's about the simplest example. And yes, the entire thing is async, hence it needed the onload handler.
You could also wrap it up in a function:
function getScript(src,callback){
var script = document.createElement('script');
script.onload = callback;
document.head.appendChild(script);
script.src = src;
}
getScript('path/to/js',function(){
//loaded
});
However, thinking out of the box, it sounds like you need dependency management. Use RequireJS. Should fit your needs.
jQuery getScript has a callback you can use to execute events after a file is loaded, or you can use script.onload as in Joseph's example:
http://api.jquery.com/jquery.getscript/
If you want to wait until a certain property is available (ie after some work has finished), you can create an interval and then clear it as soon as the property is found in window, ie:
var waitForObj = setInterval(function(){
if(window.myObj && window.myObj.somefunc){
clearInterval(waitForObj);
//Do some stuff you need this object for
}
}, 100);
I'm trying to do the following:
- from an HTML page I want to call a .js file in the header. I want this file to contain 2 functions, A and B, that I want to call at different times as the page loads.
This first part I have ready, my problem is:
-Function B is supposed to call another .js file, wait for it to complete loading and run some code specific to function B.
This is what I have so far:
<head><script type="text/javascript" src="first.js"></script></head>
I have this to call the different functions that are inside first.js
<script language="JavaScript" type="text/javascript">
functionA ();
</script>
Now inside first.js:
function functionA ()
{
alert("A runs!");
}
function functionB ()
{
alert("B runs!");
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "http://www.URL.com" ;
--some additional code--
}
Suggestions? Thanks!
First off, you don't "call a javascript file". You load the javascript file which causes it to get parsed and any top level code is executed. Once loaded, you can then call any functions in it.
So, to load a javascript file from functionB and then call a function in it, you can finish the dynamically loaded code you started with, but then you need to add a notification for when it is loaded:
function functionB () {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "http://www.URL.com" ;
script.onload = function() {
// call the function in the newly loaded scrip here
};
document.getElementsByTagName("head")[0].appendChild(script);
}
Here's a post with a loadScript function that takes a callback when the script is loaded.
Here's a post with a bit more capable function that can actually load scripts sequentially and also contains support code for older versions of IE (if that is required).
Making sure an external js file has been loaded across browsers requires more than a simple onload event; If you want a well-tested solution with added features, your best bet is using a library such as require.js.
Your code, after you include the require.js file somewhere, will look something like:
function functionA () {
alert("A runs!");
}
function functionB () {
alert("B runs!");
require(['yourotherfile'], function(myfile) { // no js extension, read more in require's docs
// --some additional code--
});
}
I have a small script
document.write("<html><head><script src='/js/jquery-1.4.2.min.js' type='text/javascript'></scr"
+ "ipt><script>alert($"+"().jquery);</scri" + "pt></head></html>");
But I get a $ is undefined in Internet Explorer. I think it tries to run the script before loading the library.
However this runs in Firefox. Please help.
Edit: I open a new window and write to that window's document.
What's wrong with creating the <script> tag the proper way? document.write is evil, end of discussion.
Try with this:
var load_script = function(options) {
options.owner_document = options.owner_document || document;
var script_tag = options.owner_document.createElement('script');
script_tag.setAttribute('type', 'text/javascript');
script_tag.setAttribute('src', options.src);
script_tag.onload = function() {
script_tag.onreadystatechange = null;
options.callback && options.callback();
};
script_tag.onreadystatechange = function() {
if (script_tag.readyState == 'loaded' || script_tag.readyState == 'complete') {
script_tag.onload = null;
options.callback && options.callback();
}
};
options.owner_document.getElementsByTagName('head')[0].appendChild(script_tag);
};
as you see, there's a simple API on that snippet:
src - source of the script
owner_document - document where the script will be inserted, defaults to the current document where the script is running from
callback - function to run after the script has loaded, anything that requires the src script is safe to be run inside this closure.
example usage:
// sample loading of jQuery
load_script({
src: '/js/jquery-1.4.2.min.js',
callback: function() {
// jQuery is available at this point, run your code.
}
});
alternatively, you can use loaders like requiere.js and LABjs
The thing is that you have unintentionally triggered IE to load your scripts in a non-blocking way. Read this for more information: Loading JavaScript without blocking.
The page suggests that you use this code to get notified when the script has finished loading:
//Internet Explorer only
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "/js/jquery-1.4.2.min.js";
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
// Your code goes here.
alert("Script is ready!");
}
};
document.body.appendChild(script);
try running your second script on page load(window.onload) , or try inserting the script in the body, not in the head section.
try :
document.write("<html><head><script src='/js/jquery-1.4.2.min.js' type='text/javascript'></scr"
+ "ipt><script>window.onload = function(){alert($"+"().jquery);};</scri" + "pt></head></html>");
the browser won't load the library instantly (unless it has it in its cache) so your script might get called before the library is loaded.
See this solution:
JavaScript's document.write Inline Script Execution Order
(also note the comment, it's important)
You could write a recursive function that waits for the jquery library to load and then executes any code once the library is loaded. You probably want to add a conditional that breaks out of the recursion if it gets too deep.
document.write("<html><head><script src='/js/jquery-1.4.2.min.js' type='text/javascript'></scr"+ "ipt><script type='text/javascript'>var wait_for_jQuery = function() {if (typeof $ === 'undefined') {setTimeout('wait_for_jQuery()', 1000);} else {alert($"+"().jquery);}}; wait_for_jQuery(); </scri" + "pt></head></html>");
Working example here: http://www.jsfiddle.net/YqTgM/36/
Have you tried moving the script tag running your code to the end of the body tag? According to Yahoo's best practices (creators of YSlow):
The problem caused by scripts is that
they block parallel downloads. The
HTTP/1.1 specification suggests that
browsers download no more than two
components in parallel per hostname.
If you serve your images from multiple
hostnames, you can get more than two
downloads to occur in parallel. While
a script is downloading, however, the
browser won't start any other
downloads, even on different
hostnames.
So the result would look like...
document.write("<html><head><script src='/js/jquery-1.4.2.min.js' type='text/javascript'></scr"
+ "ipt></head><body><script>alert($"+"().jquery);</scri" + "pt></body></html>");
Although I would also advocate not using document.write().
Give this a try?
http://jsfiddle.net/MP75r/
If you want simplified conditional loading of jQuery, there are many options out there already; no need to reinvent the wheel. Already mentioned are RequireJS and LabJS. I am using Head.js and it couldn't be easier to accomplish the goal of non-blocking loading while respecting execution order.
you are just creating a tag to reference jQuery, and as soon as its created next statement is an alert. There is no way specified by you that alert statement should run after after file load.
A simple timer should do it.
(function(){
function wait() {
if (typeof jQuery == 'undefined') {
setTimeout(wait, 50);
} else {
run(); // your code to execute
}
}
wait();
})();
Example on jsfiddle: http://jsfiddle.net/madr/Gtafq/
Inside the <script></script> tag wrap your code in a document ready function so the libraries are loaded before running.
$(document).ready(function() {
// put all your jQuery code here
});
I'm downloading JQuery asynchronously:
function addScript(url) {
var script = document.createElement('script');
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
addScript('jquery.js');
// non-jquery code ...
// jquery specific code like: $()...
As such - how do I call my JQuery specific code once JQuery is loaded (because since I'm downloading my JavaScript asynch - it's not blocking, which is good, but is trying to execute my JQuery specific code before JQuery has been loaded).
You can host a copy of the jquery file yourself. Then you can add a call to the callback function at the bottom of jquery.js:
/* jquery code goes here ... */
my_onload_callback();
For me this works (tested in FireFox 33.0.3):
if(typeof(jQuery) == "undefined"){
//create onload-callback function
window["__9384nalksdfalkj04320"] = function(){
console.log("jQuery=" + jQuery);
};
//load jQuery asynchronously
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("onload", "__9384nalksdfalkj04320();"); //register onload-callback listener function
script.setAttribute("src", "http://code.jquery.com/jquery-latest.min.js");
document.head.appendChild(script);
}
You can inline LabJs into your page (potentially, every page). On the downside, you're inlining a script over and over. On the upside, LabJs is pretty small - 4k minified - and it lets you handle complex asynchrony load patterns cross-browser with very simple code like:
<script>
// Minified LabJs goes here
</script>
<script>
function init() {
// Your code after jquery loads goes here
}
$LAB
.script('//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js')
.wait(init);
</script>
</body>
I'm not much on standard Javascript, but you may try doing something like this:
var script_object = new addScript('jquery.js');
script_object.onLoad('addScript("my_jquery_related.js")');
Admittedly, that's a mega shot in the dark.
If that doesn't work, maybe pass through your function as a callback variable in your JS loader:
addScript(url, function(){ function_to_call();})
function addScript(url, call_back_function) {
var script = document.createElement('script');
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
call_back_function.call;
}
addScript('jquery.js');
That's all I got :\