Reputation: 175563
I have a script that uses $(document).ready
, but it doesn't use anything else from jQuery. I'd like to lighten it up by removing the jQuery dependency.
How can I implement my own $(document).ready
functionality without using jQuery? I know that using window.onload
will not be the same, as window.onload
fires after all images, frames, etc. have been loaded.
Upvotes: 2452
Views: 1373694
Reputation: 55613
Edit:
function ready(fn) {
if (document.readyState !== 'loading') {
fn();
return;
}
document.addEventListener('DOMContentLoaded', fn);
}
ready(function(){
console.log("hello world")
})
From: https://youmightnotneedjquery.com/
As a one liner by Tanuki with a little tweak:
const ready = fn => document.readyState !== 'loading' ? fn() : document.addEventListener('DOMContentLoaded', fn);
function ready(callback){
// in case the document is already rendered
if (document.readyState!='loading') callback();
// modern browsers
else if (document.addEventListener) document.addEventListener('DOMContentLoaded', callback);
// IE <= 8
else document.attachEvent('onreadystatechange', function(){
if (document.readyState=='complete') callback();
});
}
ready(function(){
// do something
});
Taken from https://plainjs.com/javascript/events/running-code-when-the-document-is-ready-15/
Another good domReady function here taken from https://stackoverflow.com/a/9899701/175071
As the accepted answer was very far from complete, I stitched together a "ready" function like jQuery.ready()
based on jQuery 1.6.2 source:
var ready = (function(){
var readyList,
DOMContentLoaded,
class2type = {};
class2type["[object Boolean]"] = "boolean";
class2type["[object Number]"] = "number";
class2type["[object String]"] = "string";
class2type["[object Function]"] = "function";
class2type["[object Array]"] = "array";
class2type["[object Date]"] = "date";
class2type["[object RegExp]"] = "regexp";
class2type["[object Object]"] = "object";
var ReadyObj = {
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
// Hold (or release) the ready event
holdReady: function( hold ) {
if ( hold ) {
ReadyObj.readyWait++;
} else {
ReadyObj.ready( true );
}
},
// Handle when the DOM is ready
ready: function( wait ) {
// Either a released hold or an DOMready/load event and not yet ready
if ( (wait === true && !--ReadyObj.readyWait) || (wait !== true && !ReadyObj.isReady) ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( ReadyObj.ready, 1 );
}
// Remember that the DOM is ready
ReadyObj.isReady = true;
// If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --ReadyObj.readyWait > 0 ) {
return;
}
// If there are functions bound, to execute
readyList.resolveWith( document, [ ReadyObj ] );
// Trigger any bound ready events
//if ( ReadyObj.fn.trigger ) {
// ReadyObj( document ).trigger( "ready" ).unbind( "ready" );
//}
}
},
bindReady: function() {
if ( readyList ) {
return;
}
readyList = ReadyObj._Deferred();
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
return setTimeout( ReadyObj.ready, 1 );
}
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", ReadyObj.ready, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", DOMContentLoaded );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", ReadyObj.ready );
// If IE and not a frame
// continually check to see if the document is ready
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) {}
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
},
_Deferred: function() {
var // callbacks list
callbacks = [],
// stored [ context , args ]
fired,
// to avoid firing when already doing so
firing,
// flag to know if the deferred has been cancelled
cancelled,
// the deferred itself
deferred = {
// done( f1, f2, ...)
done: function() {
if ( !cancelled ) {
var args = arguments,
i,
length,
elem,
type,
_fired;
if ( fired ) {
_fired = fired;
fired = 0;
}
for ( i = 0, length = args.length; i < length; i++ ) {
elem = args[ i ];
type = ReadyObj.type( elem );
if ( type === "array" ) {
deferred.done.apply( deferred, elem );
} else if ( type === "function" ) {
callbacks.push( elem );
}
}
if ( _fired ) {
deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
}
}
return this;
},
// resolve with given context and args
resolveWith: function( context, args ) {
if ( !cancelled && !fired && !firing ) {
// make sure args are available (#8421)
args = args || [];
firing = 1;
try {
while( callbacks[ 0 ] ) {
callbacks.shift().apply( context, args );//shifts a callback, and applies it to document
}
}
finally {
fired = [ context, args ];
firing = 0;
}
}
return this;
},
// resolve with this as context and given arguments
resolve: function() {
deferred.resolveWith( this, arguments );
return this;
},
// Has this deferred been resolved?
isResolved: function() {
return !!( firing || fired );
},
// Cancel
cancel: function() {
cancelled = 1;
callbacks = [];
return this;
}
};
return deferred;
},
type: function( obj ) {
return obj == null ?
String( obj ) :
class2type[ Object.prototype.toString.call(obj) ] || "object";
}
}
// The DOM ready check for Internet Explorer
function doScrollCheck() {
if ( ReadyObj.isReady ) {
return;
}
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch(e) {
setTimeout( doScrollCheck, 1 );
return;
}
// and execute any waiting functions
ReadyObj.ready();
}
// Cleanup functions for the document ready method
if ( document.addEventListener ) {
DOMContentLoaded = function() {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
ReadyObj.ready();
};
} else if ( document.attachEvent ) {
DOMContentLoaded = function() {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", DOMContentLoaded );
ReadyObj.ready();
}
};
}
function ready( fn ) {
// Attach the listeners
ReadyObj.bindReady();
var type = ReadyObj.type( fn );
// Add the callback
readyList.done( fn );//readyList is result of _Deferred()
}
return ready;
})();
How to use:
<script>
ready(function(){
alert('It works!');
});
ready(function(){
alert('Also works!');
});
</script>
I am not sure how functional this code is, but it worked fine with my superficial tests. This took quite a while, so I hope you and others can benefit from it.
PS.: I suggest compiling it.
Or you can use http://dustindiaz.com/smallest-domready-ever:
function r(f){/in/.test(document.readyState)?setTimeout(r,9,f):f()}
r(function(){/*code to run*/});
or the native function if you only need to support the new browsers (Unlike jQuery ready, this won't run if you add this after the page has loaded)
document.addEventListener('DOMContentLoaded',function(){/*fun code to run*/})
Upvotes: 434
Reputation: 2045
It is worth looking in Rock Solid addEvent().
Here is the code in case the site goes down
function addEvent(obj, type, fn) {
if (obj.addEventListener) {
obj.addEventListener(type, fn, false);
EventCache.add(obj, type, fn);
}
else if (obj.attachEvent) {
obj["e"+type+fn] = fn;
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
obj.attachEvent( "on"+type, obj[type+fn] );
EventCache.add(obj, type, fn);
}
else {
obj["on"+type] = obj["e"+type+fn];
}
}
var EventCache = function(){
var listEvents = [];
return {
listEvents : listEvents,
add : function(node, sEventName, fHandler){
listEvents.push(arguments);
},
flush : function(){
var i, item;
for(i = listEvents.length - 1; i >= 0; i = i - 1){
item = listEvents[i];
if(item[0].removeEventListener){
item[0].removeEventListener(item[1], item[2], item[3]);
};
if(item[1].substring(0, 2) != "on"){
item[1] = "on" + item[1];
};
if(item[0].detachEvent){
item[0].detachEvent(item[1], item[2]);
};
item[0][item[1]] = null;
};
}
};
}();
// Usage
addEvent(window, 'unload', EventCache.flush);
addEvent(window, 'load', function(){alert("I'm ready");});
Upvotes: 7
Reputation: 483
To execute JavaScript code once the page content has fully loaded, you can use document.addEventListener
with the DOMContentLoaded
event. There are two ways to approach this: using an anonymous function or a named function.
Using an Anonymous Function:
This method is useful when you want to quickly encapsulate your code without needing to reuse it elsewhere. Here, the function is defined directly within the event listener call:
document.addEventListener('DOMContentLoaded', function () { // Your code goes here... }, false);
In this case, the anonymous function will execute once the DOM is fully loaded.
If you plan on reusing the function elsewhere or prefer a more organized code structure, you can define a named function and then pass it as a reference to the addEventListener:
document.addEventListener('DOMContentLoaded', myCode, false);
function myCode() {
// Your code goes here...
}
Here, myCode
is a function defined elsewhere and is invoked when the DOMContentLoaded
event occurs. This separation of the function definition and its assignment to the event can make the code more readable and maintainable, especially in larger applications.
Both methods are valid, and the choice between them depends on your specific use case and preference for code organization. The anonymous function is concise for quick tasks, while the named function provides better readability and reusability for more complex or reusable code.
Upvotes: 1
Reputation: 21899
In 2022, all you need to do is put the defer attribute on your script, and load it in the head!
Reference: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer
<!doctype html>
<html>
<head>
<script src="/script.js" defer></script>
</head>
<body>
<p>In 2022, all you need to do is put the defer attribute on your script, and load it in the head!</p>
</body>
</html>
Upvotes: 28
Reputation: 45382
There is a standards based replacement,DOMContentLoaded
that is supported by over 99% of browsers, though not IE8:
document.addEventListener("DOMContentLoaded", function(event) {
//do work
});
jQuery's native function is much more complicated than just window.onload, as depicted below.
function bindReady(){
if ( readyBound ) return;
readyBound = true;
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", function(){
document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
jQuery.ready();
}, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent("onreadystatechange", function(){
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", arguments.callee );
jQuery.ready();
}
});
// If IE and not an iframe
// continually check to see if the document is ready
if ( document.documentElement.doScroll && window == window.top ) (function(){
if ( jQuery.isReady ) return;
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch( error ) {
setTimeout( arguments.callee, 0 );
return;
}
// and execute any waiting functions
jQuery.ready();
})();
}
// A fallback to window.onload, that will always work
jQuery.event.add( window, "load", jQuery.ready );
}
Upvotes: 1867
Reputation: 23754
Nowadays you should use modules. Put your code into the default function of a module and import the function into a script element.
client.js
:
export default function ()
{
alert ("test");
}
index.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="module">
import main from './client.js';
main ();
</script>
</body>
</html>
Upvotes: 4
Reputation: 398
Works in all known browsers (tested via BrowserStack). IE6+, Safari 1+, Chrome 1+, Opera, etc. Uses DOMContentLoaded
, with fallbacks to document.documentElement.doScroll()
and window.onload
.
/*! https://github.com/Kithraya/DOMContentLoaded v1.2.6 | MIT License */
DOMContentLoaded.version = "1.2.6";
function DOMContentLoaded() { "use strict";
var ael = 'addEventListener', rel = 'removeEventListener', aev = 'attachEvent', dev = 'detachEvent';
var alreadyRun = false, // for use in the idempotent function ready()
funcs = arguments;
// old versions of JS return '[object Object]' for null.
function type(obj) { return (obj === null) ? 'null' : Object.prototype.toString.call(obj).slice(8,-1).toLowerCase() }
function microtime() { return + new Date() }
/* document.readyState === 'complete' reports correctly in every browser I have tested, including IE.
But IE6 to 10 don't return the correct readyState values as per the spec:
readyState is sometimes 'interactive', even when the DOM isn't accessible in IE6/7 so checking for the onreadystatechange event like jQuery does is not optimal
readyState is complete at basically the same time as 'window.onload' (they're functionally equivalent, within a few tenths of a second)
Accessing undefined properties of a defined object (document) will not throw an error (in case readyState is undefined).
*/
// Check for IE < 11 via conditional compilation
/// values: 5?: IE5, 5.5?: IE5.5, 5.6/5.7: IE6/7, 5.8: IE8, 9: IE9, 10: IE10, 11*: (IE11 older doc mode), undefined: IE11 / NOT IE
var jscript_version = Number( new Function("/*@cc_on return @_jscript_version; @*\/")() ) || NaN;
// check if the DOM has already loaded
if (document.readyState === 'complete') { ready(null); return; } // here we send null as the readyTime, since we don't know when the DOM became ready.
if (jscript_version < 9) { doIEScrollCheck(); return; } // For IE<9 poll document.documentElement.doScroll(), no further actions are needed.
/*
Chrome, Edge, Firefox, IE9+, Opera 9+, Safari 3.1+, Android Webview, Chrome for Android, Edge Mobile,
Firefox for Android 4+, Opera for Android, iOS Safari, Samsung Internet, etc, support addEventListener
And IE9+ supports 'DOMContentLoaded'
*/
if (document[ael]) {
document[ael]("DOMContentLoaded", ready, false);
window[ael]("load", ready, false); // fallback to the load event in case addEventListener is supported, but not DOMContentLoaded
} else
if (aev in window) { window[aev]('onload', ready);
/* Old Opera has a default of window.attachEvent being falsy, so we use the in operator instead
https://dev.opera.com/blog/window-event-attachevent-detachevent-script-onreadystatechange/
Honestly if somebody is using a browser so outdated AND obscure (like Opera 7 where neither addEventListener
nor "DOMContLoaded" is supported, they deserve to wait for the full page).
I CBA testing whether readyState === 'interactive' is truly interactive in browsers designed in 2003. I just assume it isn't (like in IE6-8).
*/
} else { // fallback to queue window.onload that will always work
addOnload(ready);
}
// This function allows us to preserve any original window.onload handlers (in super old browsers where this is even necessary),
// while keeping the option to chain onloads, and dequeue them.
function addOnload(fn) { var prev = window.onload; // old window.onload, which could be set by this function, or elsewhere
// we add a function queue list to allow for dequeueing
// addOnload.queue is the queue of functions that we will run when when the DOM is ready
if ( type( addOnload.queue ) !== 'array') { addOnload.queue = [];
if ( type(prev) === 'function') { addOnload.queue.push( prev ); } // add the previously defined event handler
}
if (typeof fn === 'function') { addOnload.queue.push(fn) }
window.onload = function() { // iterate through the queued functions
for (var i = 0; i < addOnload.queue.length; i++) { addOnload.queue[i]() }
};
}
// remove a queued window.onload function from the chain (simplified);
function dequeueOnload(fn) { var q = addOnload.queue, i = 0;
// sort through the queued functions in addOnload.queue until we find `fn`
if (type( q ) === 'array') { // if found, remove from the queue
for (; i < q.length; i++) { ;;(fn === q[i]) ? q.splice(i, 1) : 0; } // void( (fn === q[i]) ? q.splice(i, 1) : 0 )
}
}
function ready(ev) { // idempotent event handler function
if (alreadyRun) {return} alreadyRun = true;
// this time is when the DOM has loaded (or if all else fails, when it was actually possible to inference the DOM has loaded via a 'load' event)
// perhaps this should be `null` if we have to inference readyTime via a 'load' event, but this functionality is better.
var readyTime = microtime();
detach(); // detach any event handlers
// run the functions
for (var i=0; i < funcs.length; i++) { var func = funcs[i];
if (type(func) === 'function') {
func.call(document, { 'readyTime': (ev === null ? null : readyTime), 'funcExecuteTime': microtime() }, func);
// jquery calls 'ready' with `this` being set to document, so we'll do the same.
}
}
}
function detach() {
if (document[rel]) {
document[rel]("DOMContentLoaded", ready); window[rel]("load", ready);
} else
if (dev in window) { window[dev]("onload", ready); }
else {
dequeueOnload(ready);
}
}
function doIEScrollCheck() { // for use in IE < 9 only.
if ( window.frameElement ) {
// we're in an <iframe> or similar
// the document.documentElemeent.doScroll technique does not work if we're not at the top-level (parent document)
try { window.attachEvent("onload", ready); } catch (e) { } // attach to onload if were in an <iframe> in IE as there's no way to tell otherwise
return;
}
try {
document.documentElement.doScroll('left'); // when this statement no longer throws, the DOM is accessible in old IE
} catch(error) {
setTimeout(function() {
(document.readyState === 'complete') ? ready() : doIEScrollCheck();
}, 50);
return;
}
ready();
}
}
Usage:
<script>
DOMContentLoaded(function(e) { console.log(e) });
</script>
Upvotes: 0
Reputation: 92347
Here (in below snippet) is comparison of chosen available browser "built-in" methods and their execution sequence. Remarks
document.onload
(X) is not supported by any modern browser (event is never fired)<body onload="bodyOnLoad()">
(F) and at the same time window.onload
(E) then only first one will be executed (because it override second one)<body onload="...">
(F) is wrapped by additional onload
functiondocument.onreadystatechange
(D) not override document .addEventListener('readystatechange'...)
(C) probably cecasue onXYZevent-like
methods are independent than addEventListener
queues (which allows add multiple listeners). Probably nothing happens between execution this two handlers.div
write their timestamps also in body (click "Full Page" link after script execution to see it).readystatechange
(C,D) are executed multiple times by browser but for different document states:DOMContentLoaded
body/window onload
<html>
<head>
<script>
// solution A
console.log(`[timestamp: ${Date.now()}] A: Head script`) ;
// solution B
document.addEventListener("DOMContentLoaded", () => {
print(`[timestamp: ${Date.now()}] B: DOMContentLoaded`);
});
// solution C
document.addEventListener('readystatechange', () => {
print(`[timestamp: ${Date.now()}] C: ReadyState: ${document.readyState}`);
});
// solution D
document.onreadystatechange = s=> {print(`[timestamp: ${Date.now()}] D: document.onreadystatechange ReadyState: ${document.readyState}`)};
// solution E (never executed)
window.onload = () => {
print(`E: <body onload="..."> override this handler`);
};
// solution F
function bodyOnLoad() {
print(`[timestamp: ${Date.now()}] F: <body onload='...'>`);
infoAboutOnLoad(); // additional info
}
// solution X
document.onload = () => {print(`document.onload is never fired`)};
// HELPERS
function print(txt) {
console.log(txt);
if(mydiv) mydiv.innerHTML += txt.replace('<','<').replace('>','>') + '<br>';
}
function infoAboutOnLoad() {
console.log("window.onload (after override):", (''+document.body.onload).replace(/\s+/g,' '));
console.log(`body.onload==window.onload --> ${document.body.onload==window.onload}`);
}
console.log("window.onload (before override):", (''+document.body.onload).replace(/\s+/g,' '));
</script>
</head>
<body onload="bodyOnLoad()">
<div id="mydiv"></div>
<!-- this script must te at the bottom of <body> -->
<script>
// solution G
print(`[timestamp: ${Date.now()}] G: <body> bottom script`);
</script>
</body>
</html>
Upvotes: 4
Reputation: 989
It is year 2020 and <script>
tag has defer
attribute.
for example:
<script src="demo_defer.js" defer></script>
it specifies that the script is executed when the page has finished parsing.
https://www.w3schools.com/tags/att_script_defer.asp
Upvotes: 35
Reputation: 12561
I simply use:
setTimeout(function(){
//reference/manipulate DOM here
});
And unlike document.addEventListener("DOMContentLoaded" //etc
as in the very top answer, it works as far back as IE9 -- http://caniuse.com/#search=DOMContentLoaded only indicates as recently as IE11.
Interestingly I stumbled upon this setTimeout
solution in 2009: Is checking for the readiness of the DOM overkill?, which probably could have been worded slightly better, as I meant "is it overkill to use various frameworks' more complicated approaches to check for the readiness of the DOM".
My best explanation for why this technique works is that, when the script with such a setTimeout has been reached, the DOM is in the middle of being parsed, so execution of the code within the setTimeout gets deferred until that operation is finished.
Upvotes: 4
Reputation: 1597
I have picked the answer from PlainJS and it's working fine for me. It extends DOMContentLoaded
so that it can be accepted at all the browsers.
This function is the equivalent of jQuery's $(document).ready()
method:
document.addEventListener('DOMContentLoaded', function(){
// do something
});
However, in contrast to jQuery, this code will only run properly in modern browsers (IE > 8) and it won't in case the document is already rendered at the time this script gets inserted (e.g. via Ajax). Therefore, we need to extend this a little bit:
function run() {
// do something
}
// in case the document is already rendered
if (document.readyState!='loading') run();
// modern browsers
else if (document.addEventListener)
document.addEventListener('DOMContentLoaded', run);
// IE <= 8
else document.attachEvent('onreadystatechange', function(){
if (document.readyState=='complete') run();
});
This covers basically all possibilities and is a viable replacement for the jQuery helper.
Upvotes: 8
Reputation: 1086
Most vanilla JS Ready functions do NOT consider the scenario where the DOMContentLoaded
handler is set after the document is already loaded - Which means the function will never run. This can happen if you look for DOMContentLoaded
within an async
external script (<script async src="file.js"></script>
).
The code below checks for DOMContentLoaded
only if the document's readyState
isn't already interactive
or complete
.
var DOMReady = function(callback) {
document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback());
};
DOMReady(function() {
//DOM ready!
});
If you want to support IE aswell:
var DOMReady = function(callback) {
if (document.readyState === "interactive" || document.readyState === "complete") {
callback();
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', callback());
} else if (document.attachEvent) {
document.attachEvent('onreadystatechange', function() {
if (document.readyState != 'loading') {
callback();
}
});
}
};
DOMReady(function() {
// DOM ready!
});
Upvotes: 0
Reputation: 3418
(function(f){
if(document.readyState != "loading") f();
else document.addEventListener("DOMContentLoaded", f);
})(function(){
console.log("The Document is ready");
});
Upvotes: 0
Reputation:
Try this:
function ready(callback){
if(typeof callback === "function"){
document.addEventListener("DOMContentLoaded", callback);
window.addEventListener("load", callback);
}else{
throw new Error("Sorry, I can not run this!");
}
}
ready(function(){
console.log("It worked!");
});
Upvotes: -2
Reputation: 1601
The ready function in jQuery
does a number of things. Frankly, I don't see that point of replacing it unless you have amazingly small output from your website. jQuery
is a pretty tiny library, and it handles all sorts of cross-browser things you'll need later.
Anyway, there's little point in posting it here, just open up jQuery
and look at the bindReady
method.
It starts by calling either document.addEventListener("DOMContentLoaded")
or document.attachEvent('onreadystatechange')
depending on the event model, and goes on from there.
Upvotes: -2
Reputation: 1499
If you don't have to support very old browsers, here is a way to do it even when your external script is loaded with async attribute:
HTMLDocument.prototype.ready = new Promise(function(resolve) {
if(document.readyState != "loading")
resolve();
else
document.addEventListener("DOMContentLoaded", function() {
resolve();
});
});
document.ready.then(function() {
console.log("document.ready");
});
Upvotes: 2
Reputation: 8429
The setTimeout/setInterval solutions presented here will only work in specific circumstances.
The problem shows up especially in older Internet Explorer versions up to 8.
The variables affecting the success of these setTimeout/setInterval solutions are:
1) dynamic or static HTML
2) cached or non cached requests
3) size of the complete HTML document
4) chunked or non chunked transfer encoding
the original (native Javascript) code solving this specific issue is here:
https://github.com/dperini/ContentLoaded
http://javascript.nwbox.com/ContentLoaded (test)
this is the code from which the jQuery team have built their implementation.
Upvotes: 1
Reputation: 2131
Here's what I use, it's fast and covers all bases I think; works for everything except IE<9.
(() => { function fn() {
// "On document ready" commands:
console.log(document.readyState);
};
if (document.readyState != 'loading') {fn()}
else {document.addEventListener('DOMContentLoaded', fn)}
})();
This seems to catch all cases:
The DOMContentLoaded event is available in IE9 and everything else, so I personally think it's OK to use this. Rewrite the arrow function declaration to a regular anonymous function if you're not transpiling your code from ES2015 to ES5.
If you want to wait until all assets are loaded, all images displayed etc then use window.onload instead.
Upvotes: 3
Reputation: 18212
Cross-browser (old browsers too) and a simple solution:
var docLoaded = setInterval(function () {
if(document.readyState !== "complete") return;
clearInterval(docLoaded);
/*
Your code goes here i.e. init()
*/
}, 30);
Upvotes: 16
Reputation: 2128
It's always good to use JavaScript equivalents as compared to jQuery. One reason is one fewer library to depend on and they are much faster than the jQuery equivalents.
One fantastic reference for jQuery equivalents is http://youmightnotneedjquery.com/.
As far as your question is concerned, I took the below code from the above link :) Only caveat is it only works with Internet Explorer 9 and later.
function ready(fn) {
if (document.readyState != 'loading') {
fn();
}
else {
document.addEventListener('DOMContentLoaded', fn);
}
}
Upvotes: 6
Reputation: 316
In short, instead of the $(document).ready() used in jQuery, we can use a JavaScript method:
<script>
document.addEventListener("DOMContentLoaded", function_name, false);
function function_name(){
statements;
}
</script>
Thus, when the page is ready i.e. DOMContentLoaded only then the function function_name() will be invoked.
Upvotes: -4
Reputation: 57881
Really, if you care about Internet Explorer 9+ only, this code would be enough to replace jQuery.ready
:
document.addEventListener("DOMContentLoaded", callback);
If you worry about Internet Explorer 6 and some really strange and rare browsers, this will work:
domReady: function (callback) {
// Mozilla, Opera and WebKit
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", callback, false);
// If Internet Explorer, the event model is used
} else if (document.attachEvent) {
document.attachEvent("onreadystatechange", function() {
if (document.readyState === "complete" ) {
callback();
}
});
// A fallback to window.onload, that will always work
} else {
var oldOnload = window.onload;
window.onload = function () {
oldOnload && oldOnload();
callback();
}
}
},
Upvotes: 23
Reputation: 2382
We found a quick-and-dirty cross browser implementation of ours that may do the trick for most simple cases with a minimal implementation:
window.onReady = function onReady(fn){
document.body ? fn() : setTimeout(function(){ onReady(fn);},50);
};
Upvotes: 4
Reputation: 373
Edit of the edit of @duskwuff to support Internet Explorer 8 too. The difference is a new call to the function test of the regex and the setTimeout with an anonymous function.
Also, I set the timeout to 99.
function ready(f){/in/.test(document.readyState)?setTimeout(function(){ready(f);},99):f();}
Upvotes: -1
Reputation: 7196
If you are loading jQuery near the bottom of BODY, but are having trouble with code that writes out jQuery(<func>) or jQuery(document).ready(<func>), check out jqShim on Github.
Rather than recreate its own document ready function, it simply holds onto the functions until jQuery is available then proceeds with jQuery as expected. The point of moving jQuery to the bottom of body is to speed up page load, and you can still accomplish it by inlining the jqShim.min.js in the head of your template.
I ended up writing this code to make moving all the scripts in WordPress to the footer, and just this shim code now sits directly in the header.
Upvotes: 0
Reputation: 14575
I use this:
document.addEventListener("DOMContentLoaded", function(event) {
//Do work
});
Note: This probably only works with newer browsers, especially these: http://caniuse.com/#feat=domcontentloaded
Upvotes: 42
Reputation: 3652
If you want to support Internet Explorer 7+ (no quirks, compatibility and other pain), last Chrome, last Safari, last Firefox and no iframes - this will be enough:
is_loaded = false
callbacks = []
loaded = ->
is_loaded = true
for i in [0...callbacks.length]
callbacks[i].call document
callbacks = []
content_loaded = ->
document.removeEventListener "DOMContentLoaded", content_loaded, true
loaded()
state_changed = ->
if document.readyState is "complete"
document.detachEvent "onreadystatechange", state_changed
loaded()
if !!document.addEventListener
document.addEventListener "DOMContentLoaded", content_loaded, true
else
document.attachEvent "onreadystatechange", state_changed
dom_ready = (callback) ->
if is_loaded
callback.call document
else
callbacks.push callback
Upvotes: -7
Reputation: 9848
Three options:
script
is the last tag of the body, the DOM would be ready before script tag executesonreadystatechange
document.onreadystatechange = function () {
if (document.readyState == "complete") {
// document is ready. Do your stuff here
}
}
Source: MDN
DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
console.log('document is ready. I can sleep now');
});
Concerned about stone age browsers:
Go to the jQuery source code and use the ready
function. In that case you are not parsing+executing the whole library you're are doing only a very small part of it.
Upvotes: 228
Reputation: 312
Just add this to the bottom of your HTML page...
<script>
Your_Function();
</script>
Because, HTML documents are parsed by top-bottom.
Upvotes: 9
Reputation: 10119
Place your <script>/*JavaScript code*/</script>
right before the closing </body>
tag.
Admittedly, this might not suit everyone's purposes since it requires changing the HTML file rather than just doing something in the JavaScript file a la document.ready
, but still...
Upvotes: 102