Reputation: 5743
Let's face it, jQuery/jQuery-ui is a heavy download.
Google recommends deferred loading of JavaScript to speed up initial rendering. My page uses jQuery to set up some tabs which are placed low on the page (mostly out of initial view) and I'd like to defer jQuery until AFTER the page has rendered.
Google's deferral code adds a tag to the DOM after the page loads by hooking into the body onLoad event:
<script type="text/javascript">
// Add a script element as a child of the body
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "deferredfunctions.js";
document.body.appendChild(element);
}
// Check for browser support of event handling capability
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>
I'd like to defer loading of jQuery this way, but when I tried it my jQuery code failed to find jQuery (not completely unexpected on my part):
$(document).ready(function() {
$("#tabs").tabs();
});
So, it seems I need to find a way to defer execution of my jQuery code until jQuery is loaded. How do I detect that the added tag has finished loading and parsing?
As a corollary, it appears that asynchronous loading may also contain an answer.
Any thoughts?
Upvotes: 77
Views: 76180
Reputation: 51
I wanted to defer load jQuery on a WordPress site, so I couldn't practically update all the references using it. Instead wrote a small wrapper to queue the jQuery calls and call then whenever it's finally loaded. Stick it in the head, only 250 bytes or so, and it means jQuery can be deferred or async loaded without altering all the existing references. Made my load times much nicer.
My quick code may not work for all jQuery functions, but so far has worked with everything but one function call that I've tried. Normally I'm doing such custom stuff I don't make it available, but I thought I'd put this snippet online. Available here https://github.com/andrewtranter/jQuery_deferred_compat
Upvotes: 2
Reputation: 3600
There is a simple trick which Google Analytics uses.
1. In the head of your HTML add a tiny script
<script>
window.jQuery_store = [];
window.jQueryReady = function (fn) { jQuery_store.push(fn); }
</script>
The function jQueryReady will just save delegates into an array for future use.
2. Create a new JS script jquery-ready.js
with next content:
// When jQuery is finaly ready
$(function() {
// Replace previous implementation
window.jQueryReady = function(fn) {
// jQuery is loaded call immediately
fn();
}
// Call all saved callbacks
for (var i = 0; i < window.jQuery_store.length; ++i) {
try {
window.jQuery_store[i]();
} catch (err) {
console.log(err);
}
}
})
When this script is loaded, it will:
jQuery
is safe to usejQueryReady
with a new one, which just calls delegate immediately (the jQuery is ready at the given time).jQueryReady
calls.3. Put everything together Make jquery-ready.js load only after the jQuery is loaded. In your footer you will have:
<script defer src=".../jquery.min.js">
<script defer src=".../bootstrap.min.js">
... any other plugins for jQuery you probably need
<script defer src=".../jquery-ready.js">
This makes sure that the jquery-ready.js script will be executed only after the jQuery was executed.
You can now use jQueryReady function whenever you want.
jQueryReady(function() {
// jQuery is safe to use here
$("div.to-be-hidden").hide();
})
Upvotes: 2
Reputation: 18010
Here is a good description of modern approach for async/defer javascript loading. But it doesn't works for inline scripts
<script type="text/javascript" src="/jquery/3.1.1-1/jquery.min.js" defer></script>
<script type="text/javascript" defer>
$(function () { // <- jquery is not yet initialized
...
});
</script>
The simplest solution for async loading was suggested by @nilskp - externalize script:
<script type="text/javascript" src="/jquery/3.1.1-1/jquery.min.js" defer></script>
<script type="text/javascript" src="resources/js/onload.js" defer></script>
Upvotes: 13
Reputation: 727
I add this piece of code after the async/defered jquery script tag, this defines a temporary function $ that will accumulate whatever it is that needs to run when everything is done loading, and then once we're done use $ that by this time would be overwritten to execute the functions. With this piece of code there's no need to change the jQuery onload syntax further down in the document.
<script defer async src="https://code.jquery.com/jquery-2.2.0.min.js">
<script>
var executeLater = [];
function $(func) {
executeLater.push(func);
}
window.addEventListener('load', function () {
$(function () {
for (var c = 0; c < executeLater.length; c++) {
executeLater[c]();
}
});
})
</script>
....and then...
<script>
$(function() {
alert("loaded");
});
</script>
Upvotes: 5
Reputation: 678
I think Modernizr.load() is worth a mention here - it handles dependency loading very nicely
Upvotes: 1
Reputation: 33
Load all scripts at the end of html with http://labjs.com, it is 100% solution and I tested it many times against gtmetrix rules. example http://gtmetrix.com/reports/interactio.cz/jxomHSLV
Upvotes: -3
Reputation: 111
In certain situation you could fire an event when jquery is loaded.
<script type="text/javascript">
(function (window) {
window.jQueryHasLoaded = false;
document.body.addEventListener('jqueryloaded', function (e) {
console.log('jqueryloaded ' + new Date() );
}, false);
function appendScript(script) {
var tagS = document.createElement("script"),
s = document.getElementsByTagName("script")[0];
tagS.src = script.src;
s.parentNode.insertBefore(tagS, s);
if ( script.id == 'jquery' ) {
tagS.addEventListener('load', function (e) {
window.jQueryHasLoaded = true;
var jQueryLoaded = new Event('jqueryloaded');
document.body.dispatchEvent(jQueryLoaded);
}, false);
}
}
var scripts = [
{
'id': 'jquery',
'src': 'js/libs/jquery/jquery-2.0.3.min.js'
},
{
'src': 'js/myscript1.js'
},
{
'src': 'js/myscript2.js'
}
];
for (var i=0; i < scripts.length; i++) {
appendScript(scripts[i]);
}
}(window));
</script>
Then wrap your dependencies in a function:
// myscript1.js
(function(){
function initMyjQueryDependency() {
console.log('my code is executed after jquery is loaded!');
// here my code that depends on jquery
}
if ( jQueryHasLoaded === true )
initMyjQueryDependency();
else
document.body.addEventListener('jqueryloaded', initMyjQueryDependency, false);
}());
If jquery finishes to load after the other scripts, your dependencies will be executed when the jqueryloaded event is fired.
If jquery is already loaded, jQueryHasLoaded === true
, your dependency will be executed initMyjQueryDependency()
.
Upvotes: 2
Reputation: 16307
<!doctype html>
<html>
<head>
</head>
<body>
<p>If you click on the "Hide" button, I will disappear.</p>
<button id="hide" >Hide</button>
<button id="show" >Show</button>
<script type="text/javascript">
function loadScript(url, callback) {
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState) { //IE
script.onreadystatechange = function() {
if (script.readyState == "loaded" ||
script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function() {
callback();
};
}
script.src = url;
document.body.appendChild(script);
}
loadScript("http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js",
function() {
//YAHOO.namespace("mystuff");
$("#show").click(function() {
$("p").show();
});
$("#hide").click(function() {
$("p").hide();
});
//more...
});
</script>
</body>
</html>
Upvotes: 1
Reputation: 189
As this is a top ranking question on a important subject let me be so bold to provide my own take on this based on a previous answer from @valmarv and @amparsand.
I'm using a multi-dimensional array to load the scripts. Grouping together those that have no dependencies between them:
var dfLoadStatus = 0;
var dfLoadFiles = [
["http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"],
["http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js",
"/js/somespecial.js",
"/js/feedback-widget.js#2312195",
"/js/nohover.js"]
];
function downloadJSAtOnload() {
if (!dfLoadFiles.length) return;
var dfGroup = dfLoadFiles.shift();
dfLoadStatus = 0;
for(var i = 0; i<dfGroup.length; i++) {
dfLoadStatus++;
var element = document.createElement('script');
element.src = dfGroup[i];
element.onload = element.onreadystatechange = function() {
if ( ! this.readyState ||
this.readyState == 'complete') {
dfLoadStatus--;
if (dfLoadStatus==0) downloadJSAtOnload();
}
};
document.body.appendChild(element);
}
}
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
It loads first jquery after it is loaded it continue to load the other scripts at once. You can add scripts easy by adding to the array anywhere on your page:
dfLoadFiles.push(["/js/loadbeforeA.js"]);
dfLoadFiles.push(["/js/javascriptA.js", "/js/javascriptB.js"]);
dfLoadFiles.push(["/js/loadafterB.js"]);
Upvotes: 14
Reputation: 4314
Try this, which is something I edited a while ago from the jQuerify bookmarklet. I use it frequently to load jQuery and execute stuff after it's loaded. You can of course replace the url there with your own url to your customized jquery.
(function() {
function getScript(url,success){
var script=document.createElement('script');
script.src=url;
var head=document.getElementsByTagName('head')[0],
done=false;
script.onload=script.onreadystatechange = function(){
if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) {
done=true;
success();
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
};
head.appendChild(script);
}
getScript('http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js',function(){
// YOUR CODE GOES HERE AND IS EXECUTED AFTER JQUERY LOADS
});
})();
I would really combine jQuery and jQuery-UI into one file and use a url to it. If you REALLY wanted to load them separately, just chain the getScripts:
getScript('http://myurltojquery.js',function(){
getScript('http://myurltojqueryUI.js',function(){
//your tab code here
})
});
Upvotes: 54
Reputation: 894
Here's my version which supports chaining to be sure the scripts are loaded one after each other, based on ampersand's code:
var deferredJSFiles = ['jquery/jquery', 'file1', 'file2', 'file3'];
function downloadJSAtOnload() {
if (!deferredJSFiles.length)
return;
var deferredJSFile = deferredJSFiles.shift();
var element = document.createElement('script');
element.src = deferredJSFile.indexOf('http') == 0 ? deferredJSFile : '/js/' + deferredJSFile + '.js';
element.onload = element.onreadystatechange = function() {
if (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')
downloadJSAtOnload();
};
document.body.appendChild(element);
}
if (window.addEventListener)
window.addEventListener('load', downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent('onload', downloadJSAtOnload);
else
window.onload = downloadJSAtOnload;
Upvotes: 1
Reputation: 3426
Take a look jQuery.holdReady()
"Holds or releases the execution of jQuery's ready event." (jQuery 1.6+)
http://api.jquery.com/jQuery.holdReady/
Upvotes: 0
Reputation: 6949
element.addEventListener("load", function () {
$('#tabs').tabs()
}, false);
Try that.
Upvotes: 3
Reputation: 27460
Appears that you just need <script defer>
: http://www.w3schools.com/tags/att_script_defer.asp
Upvotes: 0
Reputation: 1638
The following code should load your scripts after the window is finished loading:
<html>
<head>
<script>
var jQueryLoaded = false;
function test() {
var myScript = document.createElement('script');
myScript.type = 'text/javascript';
myScript.async = true;
myScript.src = jQueryLoaded ? 'http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.js' : 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js';
document.body.appendChild(myScript);
if(!jQueryLoaded){
alert('jquery was loaded');
jQueryLoaded = true;
test();
} else {
alert('jqueryui was loaded');
}
}
if (window.addEventListener){
alert('window.addEventListener');
window.addEventListener("load", test, false);
} else if (window.attachEvent){
alert('window.attachEvent');
window.attachEvent("onload", test);
} else{
alert('window.onload');
window.onload = test;
}
</script>
</head>
<body>
<p>Placeholder text goes here</p>
</body>
</html>
Worked for me in Chrome, FF and IE9 - let me know if that helps
Upvotes: 1
Reputation: 13302
Well it seems to me, all you have to do is either a) add the jQuery code you want to run on load, to the end of the jQuery file or b) append it to the downloadJSAtOnload
function like so:
<script type="text/javascript">
// Add a script element as a child of the body
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "deferredfunctions.js";
document.body.appendChild(element);
$("#tabs").tabs(); // <==== NOTE THIS. This should theoretically run after the
// script has been appended, though you'll have to test this
// because I don't know if the JavaScript above will wait for
// the script to load before continuing
}
// Check for browser support of event handling capability
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>
Upvotes: 1
Reputation: 885
Put jQuery and your jQuery dependent code at the end of your HTML file.
Edit: A little more clear
<html>
<head></head>
<body>
<!-- Your normal content here -->
<script type="text/javascript" src="http://path/to/jquery/jquery.min.js"></script>
<script>//Put your jQuery code here</script>
</body>
</html>
Upvotes: 2