Defer functions until jQuery has loaded (or another fixed point) - javascript

Question: is there anything wrong with using the following method to defer running code until after jQuery has loaded (or until another fixed point)?
Bonus question: is there a better way to do it? (E.g. not having to name the functions in window.deferred, like window.deferred.afunction)
In my page (before jQuery):
<script>
window.deferred = window.deferred || {};
window.deferred.afunction = function() {
console.log('afunction is running');
};
window.deferred.anotherfunction = function() {
console.log('anotherfunction is running');
};
</script>
In my JS (after jQuery when I'm ready to execute my deferred functions):
/* Run deferred functions */
if(window.deferred && typeof window.deferred === 'object') {
for (var fn in window.deferred) {
if (typeof window.deferred[fn] == 'function') {
window.deferred[fn]();
}
}
}
Demo: https://jsfiddle.net/j4sqy5kf/4/
As far as I can tell it works fine. But I consider myself amateur at JavaScript - I can write it but don't always understand how it's working.
So far all the ways I've found to do this (e.g. http://snipplr.com/view/54863/) rely on having setTimeout or setInterval and calling a check function, which seems quite inelegant to me.
There's also Require.js, which I believe could be used, but I think it's overkill to achieve exactly what I want as it primarily serves a different purpose.
Background
I'm writing a post for my blog in Markdown, and I'm writing a little JavaScript inside the page to manage some hidden content. I want to use jQuery in my script, but jQuery is loaded in the footer - so, I want an elegant way to defer execution of my inline script until after jQuery has loaded.
I have jQuery in my footer and a general.js script, in which most of the rest of my JavaScript runs. I'd like to run the 'In my page' bits inside each blog post, and leave the 'In my JS' bit in general.js so I never have to worry about it again.
I know I could define but not execute the functions, but I don't want to have to update general.js each time (e.g. add function1(), function2(), etc.)
I want it to automatically loop through all deferred functions.

I don't really see anything wrong with doing it this way. I would however use an array instead of an object:
/* Run deferred functions */
if(window.deferred && Array.isArray(window.deferred)) {
window.deferred.forEach(function (fn) {
if (typeof fn === 'function') {
fn();
}
});
}
.hello-world {
color: #f00;
font-size: 3em;
font-family: verdana;
}
.jq {
color: #00f;
}
<script>
window.deferred = window.deferred || [];
window.deferred.push(function aFunction() {
console.log('aFunction is running');
});
window.deferred.push(function anotherfunction () {
console.log('anotherfunction is running');
});
window.deferred.push(function doSomethingWithJquery () {
jQuery('<div class="hello-world">Hello from <span class="jq">jQuery</span>!</div>').appendTo('body');
});
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
If you ever are looping over an object with for...in like your original code does, you should use hasOwnProperty to check that the object actually contains that property and you are not getting a property from the prototype chain.

An easier way of achieving what you want is by using DOMContentLoaded.
<script>
window.addEventListener('DOMContentLoaded', function() {
$( document ).ready(function() {
console.log('my other scripts including jquery has loaded now');
});
});
</script>
But I would stop and think about the reason for preferring inline scripts instead of your general.js, as inline scripts has a negative impact on performance, and prevents caching. If it is code separation you're after, you can achieve it by other means, depending on your setup.

Related

Best way to solve this "ReferenceError: Function is not defined"?

I have an AEM page with multiple components, these components have a .js file with a function that encloses all the client side logic. We then call that function inside the component's HTML:
<script>
window.bootstrap_component(function() {
init_component_name();
});
</script>
As stated before init_component_name is the name of a function that encompasses all the logic we need:
function init_component_name() {
//DO STUFF
}
The wrapper bootstrap_component fuction is defined in the shared head.html of all our pages as:
<script>
window.bootstrap_component = function (handler) {
if (typeof handler === 'function') {
if (document.readyState === "complete" || document.readyState === "loaded" || document.readyState === "interactive") {
handler();
} else {
document.addEventListener("DOMContentLoaded", function() {
handler();
});
}
}
}
</script>
This works okay and we don't have any actual issues but we recently started using Bugsnag for error monitoring and reporting and we're getting reports for almost every component saying ReferenceError on page so/and/so init_component_name() is not defined.
The reason I think this is happening is because the init_component_name() function is not declared within the script tag and because this, function(init_component_name) has been attached to the window object it is executing fine and you don't see any console errors.
If I am correct would modifying those scripts tags to be like this work?
<script>
window.bootstrap_component(function() {
window.init_component_name();
})
</script>
A colleague of mine wants to add a timeout to the init_component_name functions of like 1ms but it rubs me the wrong way. Is there a more sensible approach?
If I am correct would modifying those scripts tags to be like this work?
window.bootstrap_component(function() {
window.init_component_name();
})
Yes, but you then have the problem that you're writing multiple data to the global namespace, window, which isn't ideal. What if another third-party script decides to override it?
Ideally you'd have a single namespace and put everything on there, and write only that namespace to window.
window.something = {};
something.bootstrap_component = { //...
and
something.init_component_name = () => {
//DO STUFF
}
Or better still, use modules (though that will need some light code refactoring).
Don't do the timeout hack. It's really, really horrible; what if scripts take longer than a second to load for some reason? You're also forcing your UI to wait a second, often unnecessarily. This hack tends to feature where chronology and scope has not been thought out properly.

Is having 100 document ready better or worse than 1 document ready?

Just wondering if the amount of document.ready calls affects page load speed.
Is there a way in Gulp / Grunt to uglify / minify JS by removing seperate document ready functions?
Just check it!
I don't see significant difference in Chrome.
As I know, it was critical for IE8, but didn't check this fact.
IE11 shows 2 seconds on the first snippet, when the others take 200 ms only.
Also, seems like jQuery already aggregates load events.
Don't forget
When you are running same code in one tab, browser remembers something and runs it faster.
Reload the page is not enought. Open a new tab instead.
After opening a new tab, run snippets in different order.
If the snippet is ran first on the tab, it will get additional slowdown, comparing the other three.
for (var q=0; q<1000; ++q) {
document.addEventListener('DOMContentLoaded', (function (i) {
console.log(i);
}).bind(null, q));
}
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('output').textContent = performance.now().toFixed(3);
});
<output></output>
document.addEventListener('DOMContentLoaded', function () {
for (var q=0; q<1000; ++q) {
(function (i) {
console.log(i)
}).bind(null, q)();
document.querySelector('output').textContent = performance.now().toFixed(3);
}
});
<output></output>
for (var q=0; q<1000; ++q) {
$((function (i) {
console.log(i);
}).bind(null, q));
}
$(function () {
document.querySelector('output').textContent = performance.now().toFixed(3);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<output></output>
$(function () {
for (var q=0; q<1000; ++q) {
(function (i) {
console.log(i)
}).bind(null, q)();
document.querySelector('output').textContent = performance.now().toFixed(3);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<output></output>
Maybe it's just me as a JavaScript avoider, but none of the scripts have document.ready inside. If you JS guys talk about document.ready, that's a synonym for addEventListener('DOMContentLoaded')?
There are two events: DOMContentLoaded and load (window.onload). First of them occures when the body pasring is complete, but some assets are loading still. The second - when the page is completely loaded. First one is nice for running scripts with dom manipulations, but browsers not always had support of it.
So jQuery uses the first of these two events and classic form of subscription was
$(document).ready(function () {
// ...
});
but after some versions if was simplified to passing function directly into jQuery:
$(function () {
// ...
});
So in vanilla examples I'm using the first of 2 events, and in jQuery examples I'm using the short form of subscription on it. As browsers without support of this event are very old it's correct to assume that jQuery always uses DOMContentLoaded (probably the load way is removed in version 2 - didn't check it, but see no reasons to keep it there).
Many document ready calls shouldn't affect much the application performance. The best solution may be having only one and init there all you need. But it depends on your application structure and you should be more confortable having more than one. Anyway, I don't think there is any Gulp task that wraps different ready functions in one, because it will touch the application logic.
You can have multiple ones, but it's not always the neatest thing to do. Try not to overuse them, as it will seriously affect readability. Other than that , it's perfectly legal.
It's also worth noting that a function defined within one $(document).ready block cannot be called from another $(document).ready block.
$(document).ready(function() {
alert('hello1');
function saySomething() {
alert('something');
}
saySomething();
});
$(document).ready(function() {
alert('hello2');
saySomething();
});
output was
hello1
something
hello2
Check this post and this one
Yes, you can use multiple document ready handler, there is no special advantage even though you can use jQuery code in several place. You can’t use the variable inside one in another since those are in different scope.
Actually jQuery event handler pushing function for execution in
queue of a particular event. When event is fired all functions
executes one by one from particular events row/stack/queue based on
return value of parent sequential function.
BUT
There is one thing to note that each $(document).ready() function call
must return. If an exception is thrown in one, subsequent calls will
never be run.
$(document).ready(function() {
document.write('<h3>In First ready function</h3>');
var foo = function() {
console.log('inside foo');
}
document.write("foo:" +(typeof foo)+"<br>");
document.write("bar:" +(typeof bar)+"<br>");
});
$(document).ready(function() {
document.write('<h3>In Second ready function</h3>');
var bar=function bar() {
console.log('inside bar');
}
document.write("foo:" +(typeof foo)+"<br>");
document.write("bar:" +(typeof bar)+"<br>");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Actually jQuery $(document).ready() method is attach function with DOMContentLoaded event using addEventListener method.
Yes you can have multiple instance of it on a single page. There is no particular advantage. All will get executed on first called first run basis.

Handling code which relies on jQuery before jQuery is loaded

I'd like to follow the general guideline of putting all JavaScript at the very bottom of the page, to speed up loading time and also to take care of some pesky issues with conflicting jQuery versions in a web app (Django).
However, every so often I have some code, code which depends on jQuery, but which must be further up on the page (basically the code can't be moved to the bottom).
I'm wondering if there's an easy way to code this so that even though jQuery is not yet defined the code works when jQuery is defined.
The following seems, I have to say, like overkill but I don't know of another way to do it:
function run_my_code($) {
// jquery-dependent code here
$("#foo").data('bar', true);
}
var t = null;
function jquery_ready() {
if (window.jQuery && window.jQuery.ui) {
run_my_code(window.jQuery);
} else {
t = window.setTimeout(jquery_ready, 100);
}
}
t = window.setTimeout(jquery_ready, 100);
Actually, I might need to use code more than once in a page, code that doesn't know about other code, so even this probably won't work unless I rename each jquery_ready to something like jquery_ready_guid, jquery_ready_otherguid and so on.
Clarification
Just so this is clear, I am putting the include to JavaScript (<script type="text/javascript" src="jquery.min.js" />) at the very bottom of the page, just before the </body>. So I can't use the $.
Simple use pure javascript version of $(document).ready();:
document.addEventListener("DOMContentLoaded", function(event) {
//you can use jQuery there
});
Your way is the only way that I know of, though I would ensure that the scoping is a little tighter:
(function() {
var runMyCode = function($) {
// jquery-dependent code here
$("#foo").data('bar', true);
};
var timer = function() {
if (window.jQuery && window.jQuery.ui) {
runMyCode(window.jQuery);
} else {
window.setTimeout(timer, 100);
}
};
timer();
})();
Update
Here's a little deferred loader I cobbled together:
var Namespace = Namespace || { };
Namespace.Deferred = function () {
var functions = [];
var timer = function() {
if (window.jQuery && window.jQuery.ui) {
while (functions.length) {
functions.shift()(window.jQuery);
}
} else {
window.setTimeout(timer, 250);
}
};
timer();
return {
execute: function(onJQueryReady) {
if (window.jQuery && window.jQuery.ui) {
onJQueryReady(window.jQuery);
} else {
functions.push(onJQueryReady);
}
}
};
}();
Which would then be useable like so:
Namespace.Deferred.execute(runMyCode);
The best way I have found is to write the code in a function and call the function after jquery is loaded:
function RunAfterjQ(){
// Codes that uses jQuery
}
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
RunAfterjQ();
</script>
Update: For master pages, you can define an array to push functions in the head of the master page:
var afterJQ = [];
then at the bottom of master page run all the functions pushed in to this array:
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
for(var i = 0; i < afterJQ.length; i++) afterJQ[i]();
</script>
Everywhere that you need to use javascript that relies on jQuery and is before jQuery is defined just push it in to this array:
afterJQ.push( function() {
// this code will execute after jQuery is loaded.
});
Here is a way to write injected code that will be run only after jQuery loads (whether synchronously or asynchronously).
<script>
if ( ! window.deferAfterjQueryLoaded ) {
window.deferAfterjQueryLoaded = [];
Object.defineProperty(window, "$", {
set: function(value) {
window.setTimeout(function() {
$.each(window.deferAfterjQueryLoaded, function(index, fn) {
fn();
});
}, 0);
Object.defineProperty(window, "$", { value: value });
},
configurable: true
});
}
window.deferAfterjQueryLoaded.push(function() {
//... some code that needs to be run
});
</script>
What this does is:
Defines deferAfterjQueryLoaded lazily, so you don't need to inject that into head.
Defines a setter for window.$. When jQuery loads, one of the last things it does is assign to the global $ variable. This allows you to trigger a function when that happens.
Schedules the deferred functions to run as soon as possible after the jQuery script finishes (setTimeout(..., 0);).
Has the setter remove itself.
For complete cleanliness you could have the scheduled function remove deferAfterjQueryLoaded as well.
How about:
<script>
window.deferAfterjQueryLoaded = [];
window.deferAfterjQueryLoaded.push(function() {
//... some code that needs to be run
});
// ... further down in the page
window.deferAfterjQueryLoaded.push(function() {
//... some other code to run
});
</script>
<script src="jquery.js" />
<script>
$.each(window.deferAfterjQueryLoaded, function(index, fn) {
fn();
});
</script>
This works because every script here is completely blocking. Meaning the creation of the deferAfterjQueryLoaded array and all functions being created and pushed to that array occur first. Then jQuery completely loads. Then you iterate through that array and execute each function. This works if the scripts are in separate files as well just the same way.
If you ALSO want DOMReady to fire you can nest a $(function() {}) inside of one of your deferAfterjQueryLoaded functions like such:
window.deferAfterjQueryLoaded.push(function() {
$(function() {
console.log('jquery loaded and the DOM is ready');
});
console.log('jquery loaded');
});
Ultimately, you should really refactor your code so everything is actually down at the bottom, and have a system conducive to that model. It is much easier to understand everything occurring and more performant (especially if you have separate scripts).
I have this same problem, but with a ton of files, I use headjs to manage the loading, it's only 2kb so there isn't really a problem, for me anyway, of putting in the header. Your code then becomes,
head.ready(function(){
$...
});
and at the bottom of the page,
head.js('/jquery.min.js');
You should be able to do this on a document ready event.
function jQueryGodot(code)
{
if (window.jQuery)
{
code(window.jQuery);
}
else
{
if (!window.$)
{
window.$ = { codes: [] };
window.watch('$', function(p, defered, jQuery) {
jQuery.each(defered.codes, function(i, code) {
code(jQuery);
});
return jQuery;
});
}
window.$.codes.push(code);
}
}
jQueryGodot(function($) {
$('div').html('Will always work!');
})
Working example on JSFiddle.
Code passed to jQueryGodot function will always be executed no matter if it is called before or after jQuery is loaded.
The solution relies on Object.watch which requires this polyfill (660 bytes minified) in most of the browsers: https://gist.github.com/adriengibrat/b0ee333dc1b058a22b66
You can defer all calls like jQuery(function(){...}) without the loop of a setTimeout: https://jsfiddle.net/rL1f451q/3/
It is collecting every jQuery(...) call into an array until jQuery is not defined, then the second code executes them when jQuery is available.
Put this in the head or the beginning of the body:
<!-- jQuery defer code body: deferring jQuery calls until jQuery is loaded -->
<script>
window.jQueryQ = window.jQueryQ || [];
window.$ = window.jQuery = function(){
window.jQueryQ.push(arguments);
}
</script>
<!-- end: jQuery defer code body -->
And this at the very end of the body, after the jQuery script:
<!-- jQuery deferring code footer: add this to the end of body and after the jQuery code -->
<script>
jQuery(function(){
jQuery.each(window.jQueryQ||[],function(i,a){
// to understand why setTimeout 0 is useful, see: https://www.youtube.com/watch?v=8aGhZQkoFbQ, tldr: having a lot of calls wont freeze the website
setTimeout(function(){
jQuery.apply(this,a);
},0);
});
});
</script>
<!-- end: jQuery deferring code footer -->
I've wrote simple js code for handle such cases: https://github.com/Yorkii/wait-for
Then you can use it like this
waitFor('jQuery', function () {
//jQuery is loaded
jQuery('body').addClass('done');
});
You can even wait for multiple libraries
waitFor(['jQuery', 'MyAppClass'], function () {
//Both libs are loaded
});
I'm posting my answer using a JavaScript promise. It works, it is simple and reusable.
The advantage is, that the code get's executed as soon as jQuery is loaded, no matter how early or late on the page.
But, I'm by far not as experienced like some other people in this thread. So I'd love my answer to be peer reviewed. I'd like to know if it really is a valid solution and what the downsides are.
Write an objectExists function with a promise as high up in the code as you want.
function jqueryExists() {
return new Promise(function (resolve, reject) {
(function waitForJquery() {
if (jQuery) return resolve();
setTimeout(waitForJquery, 30);
})();
});
}
Then add your function which depends on jQuery. As far as I understand, it won't block any other script from execution, and the page will load just fine even if jQuery is loaded much later.
jqueryExists().then(function(){
// Write your function here
}).catch(function(){
console.log('jQuery couldn\'t be loaded');
})
jQuery can now be loaded after this code, and the function will execute as soon as jQuery is available.

Trigger $document.ready (so AJAX code I can't modify is executed)

My requirements are the following:
I've got a rich webpage that at a certain moment loads a bunch of HTML in a div, via AJAX.
The HTML I retrieve does have javascript (<script>...</script>)
The retrieved javascript contains $('document').ready( ... ) parts
I can not modify the retrieved javascript; it comes from an external lib
I've got a javascript function that is called when the AJAX is loaded. I'm trying to "trick it" into executing by doing:
function AjaxLoaded() {
$('document').trigger('ready');
}
That doesn't cut it, I'm afraid.
I've seen several responses on Stack Overflow that "evade" this question by changing the code that is returned on the AJAX (make it a function and call it after loading, or just remove the $(document).ready()). I need to stress out that I can't change the retrieved code on this case.
Afer some research i created a way to get it to work.
here is my test that shows it working: http://www.antiyes.com/test/test2.php
here is the relevant code:
<script>
// easy copy of an array
Array.prototype.copy = function() {
return [].concat(this);
};
// this function is added to jQuery, it allows access to the readylist
// it works for jQuery 1.3.2, it might break on future versions
$.getReadyList = function() {
if(this.readyList != null)
this.myreadylist = this.readyList.copy();
return this.myreadylist;
};
$(document).ready(function() {
alert("blah");
});
</script>
<script>
// this should be added last so it gets all the ready event
$(document).ready(function() {
readylist = $.getReadyList();
});
</script>
then in the body I have:
<input type="button" onclick="$(readylist).each(function(){this();});" value="trigger ready" />
basically what i did was add a function to jQuery that copies the readyList before it's cleared out, then it will be available to be used by you.
it looks like the code below doesnt work:
function AjaxLoaded() {
$(document).trigger('ready');
}
drop the quotes around document.
Since the jQuery readyList is not exposed as of version 1.4 (discussed here) the nice solutions above are broken.
A way around this is by creating your own readyList, through overriding the original jQuery-ready method. This needs to be done before other scripts that use the original ready method are loaded. Otherwise just the same code as John/Kikito:
// Overrides jQuery-ready and makes it triggerable with $.triggerReady
// This script needs to be included before other scripts using the jQuery-ready.
// Tested with jQuery 1.7
(function(){
var readyList = [];
// Store a reference to the original ready method.
var originalReadyMethod = jQuery.fn.ready;
// Override jQuery.fn.ready
jQuery.fn.ready = function(){
if(arguments.length && arguments.length > 0 && typeof arguments[0] === 'function') {
readyList.push(arguments[0]);
}
// Execute the original method.
originalReadyMethod.apply( this, arguments );
};
// Used to trigger all ready events
$.triggerReady = function() {
$(readyList).each(function(){this();});
};
})();
I'm not sure whether it is advisable to override the ready method. Feel free to advise me on that. I have not yet found any side effects myself though.
Just in case anyone needs it, I refined John's solution a bit so it could be used directly as an included javascript file.
// jquery_trigger_ready.js
// this function is added to jQuery, it allows access to the readylist
// it works for jQuery 1.3.2, it might break on future versions
$.getReadyList = function() {
if(this.readyList != null) { this.myreadylist = [].concat(this.readyList); }
return this.myreadylist;
};
$(document).ready(function() {
readylist = $.getReadyList();
});
$.triggerReady = function() {
$(readylist).each(function(){this();});
}
Including this file after including jquery allows for triggering ready by invoking $.triggerReady(). Example:
<html>
<head>
<title>trigger ready event</title>
<script src="test2_files/jquery-1.js" type="text/javascript"></script>
<script src="jquery_trigger_ready.js" type="text/javascript"></script>
</head>
<body>
<input onclick="$.triggerReady();" value="trigger ready" type="button">
<script type="text/javascript">
$(document).ready(function(){
alert("blah");
});
</script>
</body>
</html>
By the way, I wanted to make it $(document).triggerReady(). If anyone is willing to share some advice on that, ill be appreciated.
We had the same problem and solved it another way.
Instead of
$(document).ready(function () {
$('.specialClass').click(....
We used :
$(document).bind('ready', function(event) {
$('.specialClass', event.target).click(..
jQuery will trigger a "ready" event on the document as usual. When we load the content of a new div via ajax, we can write:
loadedDiv.trigger('ready')
And have all the initialization performed only on the div, obtaining what expected.
Simone Gianni's Answer I think is the most elegant and clean.
and you can even simplify it to become even more easy to use:
jQuery.fn.loadExtended = function(url,completeCallback){
return this.load(url,function(responseText, textStatus, XMLHttpRequest) {
if (completeCallback !== undefined && completeCallback !== null) {
completeCallback(responseText, textStatus, XMLHttpRequest);
}
$(this).trigger("ready");
});
};
So, now instead of using:
$(".container").load(url,function(responseText, textStatus, XMLHttpRequest) {
$(this).trigger("ready");
});
you can just use:
$(".container").loadExtended("tag_cloud.html");
or:
$(".container").loadExtended("tag_cloud.html",function(){
alert('callback function')
});
This has the advantage of only applying the trigger on the div that's being updated.
If your new loaded HTML contain <script> elements and you try insert it into main HTML with pure JS (element.innerHTML = newHTML), then $(document).ready handlers at newHTML and wrapped functions like (function() { /* some functions */ })(); - will not execute because JQuery unbind 'ready' event after first triggering and you can not trigger it repeatly. PS. But you can use $.holdReady(true) and trigger when need.
So, try insert code with jquery method, $(element).html(newHTML). This solved similar problem for me, seems jquery handle js before inserting. Using this method you also will not see the <script> elements among DOM nodes (at browser's Elements Inspector for ex.)

How to detect if javascript files are loaded?

Is there an event that fires when JavaScript files are loaded? The problem came up because YSlow recommends to move JavaScript files to the bottom of the page. This means that
$(document).ready(function1) is fired before the js file that contains the code for function1 is loaded.
How to avoid this kind of situation?
I don't have a reference for it handy, but script tags are processed in order, and so if you put your $(document).ready(function1) in a script tag after the script tags that define function1, etc., you should be good to go.
<script type='text/javascript' src='...'></script>
<script type='text/javascript' src='...'></script>
<script type='text/javascript'>
$(document).ready(function1);
</script>
Of course, another approach would be to ensure that you're using only one script tag, in total, by combining files as part of your build process. (Unless you're loading the other ones from a CDN somewhere.) That will also help improve the perceived speed of your page.
EDIT: Just realized that I didn't actually answer your question: I don't think there's a cross-browser event that's fired, no. There is if you work hard enough, see below. You can test for symbols and use setTimeout to reschedule:
<script type='text/javascript'>
function fireWhenReady() {
if (typeof function1 != 'undefined') {
function1();
}
else {
setTimeout(fireWhenReady, 100);
}
}
$(document).ready(fireWhenReady);
</script>
...but you shouldn't have to do that if you get your script tag order correct.
Update: You can get load notifications for script elements you add to the page dynamically if you like. To get broad browser support, you have to do two different things, but as a combined technique this works:
function loadScript(path, callback) {
var done = false;
var scr = document.createElement('script');
scr.onload = handleLoad;
scr.onreadystatechange = handleReadyStateChange;
scr.onerror = handleError;
scr.src = path;
document.body.appendChild(scr);
function handleLoad() {
if (!done) {
done = true;
callback(path, "ok");
}
}
function handleReadyStateChange() {
var state;
if (!done) {
state = scr.readyState;
if (state === "complete") {
handleLoad();
}
}
}
function handleError() {
if (!done) {
done = true;
callback(path, "error");
}
}
}
In my experience, error notification (onerror) is not 100% cross-browser reliable. Also note that some browsers will do both mechanisms, hence the done variable to avoid duplicate notifications.
When they say "The bottom of the page" they don't literally mean the bottom: they mean just before the closing </body> tag. Place your scripts there and they will be loaded before the DOMReady event; place them afterwards and the DOM will be ready before they are loaded (because it's complete when the closing </html> tag is parsed), which as you have found will not work.
If you're wondering how I know that this is what they mean: I have worked at Yahoo! and we put our scripts just before the </body> tag :-)
EDIT: also, see T.J. Crowder's reply and make sure you have things in the correct order.
Take a look at jQuery's .load() http://api.jquery.com/load-event/
$('script').load(function () { });
Further to #T.J. Crowder 's answer, I've added a recursive outer loop that allows one to iterate through all the scripts in an array and then execute a function once all the scripts are loaded:
loadList([array of scripts], 0, function(){// do your post-scriptload stuff})
function loadList(list, i, callback)
{
{
loadScript(list[i], function()
{
if(i < list.length-1)
{
loadList(list, i+1, callback);
}
else
{
callback();
}
})
}
}
Of course you can make a wrapper to get rid of the '0' if you like:
function prettyLoadList(list, callback)
{
loadList(list, 0, callback);
}
Nice work #T.J. Crowder - I was cringing at the 'just add a couple seconds delay before running the callback' I saw in other threads.
I always make a call from the end of the JavaScript files for registering its loading and it used to work perfect for me for all the browsers.
Ex: I have an index.htm, Js1.js and Js2.js. I add the function IAmReady(Id) in index.htm header and call it with parameters 1 and 2 from the end of the files, Js1 and Js2 respectively. The IAmReady function will have a logic to run the boot code once it gets two calls (storing the the number of calls in a static/global variable) from the two js files.
Change the loading order of your scripts so that function1 was defined before using it in ready callback.
Plus I always found it better to define ready callback as an anonymous method then named one.
Like T.J. wrote: the order is defined (at least it's sequential when your browser is about to execute any JavaScript, even if it may download the scripts in parallel somehow). However, as apparently you're having trouble, maybe you're using third-party JavaScript libraries that yield some 404 Not Found or timeout? If so, then read Best way to use Google’s hosted jQuery, but fall back to my hosted library on Google fail.

Categories

Resources