2176

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.

Kamil Kiełczewski
  • 53,729
  • 20
  • 259
  • 241
FlySwat
  • 160,042
  • 69
  • 241
  • 308
  • 299
    ...and also definitely not the same functionality. – Joel Mueller Jun 03 '09 at 19:39
  • 42
    As [this answer](http://stackoverflow.com/a/1861233/901048) states, if all you want from jQuery is `$(document).ready`, you can solve that problem easily by running your code at the very bottom of the page instead of at the top. [HTML5Boilerplate](http://html5boilerplate.com/) uses this exact approach. – Blazemonger Jun 07 '13 at 14:59
  • 5
    Why not to just use the DOMContentLoaded? It's IE9+ http://caniuse.com/domcontentloaded https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded – Brock Jul 09 '14 at 16:42
  • I placed my call at last of document and that solve my problem. When the function is called, everything is loaded. – IgniteCoders Aug 20 '14 at 18:12
  • Worth a look: https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState – Andrew Apr 01 '20 at 18:30
  • https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event – Kithraya Sep 21 '20 at 18:30

39 Answers39

1617

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 );
}
LandonSchropp
  • 9,020
  • 16
  • 75
  • 143
Chad Grant
  • 40,129
  • 8
  • 59
  • 76
  • 21
    An actual working plain javascript implementation here if someone wants code they can just drop in: http://stackoverflow.com/questions/9899372/pure-javascript-equivalent-to-jquerys-ready-how-to-call-a-function-when-the/9899701#9899701 – jfriend00 Dec 13 '14 at 07:58
  • 4
    jQuery DOM ready code appears to be simplified: https://github.com/jquery/jquery/blob/master/src/core/ready.js – Jose Nobile May 22 '15 at 01:13
  • 3
    @JoseNobile because they dropped older browser support – huysentruitw Aug 13 '15 at 08:16
  • 17
    I think we're all ready to move on from IE8... ;). Thanks for the link, @JoseNobile. – Con Antonakos Sep 18 '15 at 16:26
  • Seems to be a pretty safe bet. .6% use of IE8 globally: http://caniuse.com/#feat=domcontentloaded – phirschybar Jun 21 '16 at 13:11
  • 18
    DOMContentLoaded will not work if script is loaded afterwards. JQuery document ready executes always. – Jared Insel Nov 16 '16 at 17:03
  • `bindReady` old impl:https://github.com/jquery/jquery/blob/714b8ffd2b28af446fea8f25e369597d7c509cb4/src/core.js#L424 – ipcjs Apr 18 '17 at 23:22
  • Is it also possible to use something like `onDOMContentLoaded` statically in the HTML on the `html` or `body` tag? Or can it only be done in JavaScript? And why? (-: – hippietrail Aug 05 '17 at 01:23
  • what happens in IE8 if I use the DOMContentLoaded example? Does the entire javascript not work or does it just load whenever it wants? – carl Oct 22 '17 at 06:41
  • Depending on the DOM size, this function may unfortunately never run if the file is loaded `async`. A solution to this is to check for the document's `readyState` if the DOM is already `complete` or `interactive`. See https://stackoverflow.com/a/55649686/9314312 – Null Apr 12 '19 at 10:42
  • IE8 and lower are 0.49% only as of November 2020. This is now 99.5% support globally. – hammady Nov 18 '20 at 12:02
  • 1
    @hippietrail The equivalent of using DOMContentLoaded with html is just placing the script at the very bottom of the page, so it will fire at the same time as DOMContentLoaded. DOMContentLoaded is basically an event invented to allow scripts to be loaded higher up the page but still have access to all the content below as well. – Stijn de Witt Feb 24 '21 at 00:46
357

Edit:

Here is a viable replacement for jQuery ready

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*/})
Timo Huovinen
  • 46,329
  • 33
  • 128
  • 127
  • 15
    @TimoHuovinen Alternatives: Zepto.js (9.1 kb), Snack.js (8.1 kb), $dom (2.3 kb), and 140 Medley (0.5 kb). *Edit:* You could also take a look at Ender. – Frederik Krautwald Oct 11 '14 at 23:42
  • 2
    @FrederikKrautwald $dom sounds like what I would want, but not sure if it fits the bill. Zepto also looks really promising, thank you for sharing! – Timo Huovinen Oct 13 '14 at 20:41
  • @TimoHuovinen If you haven’t looked at Ender, you should definitely take a look, http://enderjs.com/. – Frederik Krautwald Oct 17 '14 at 20:53
  • You may also use [this code from jQuery.parts](https://github.com/addyosmani/jquery.parts/blob/master/jquery.documentReady.js) if you want the ready function exactly like jQuery without the full library. – dotpush Dec 10 '14 at 22:45
  • @dotpush wouldn't it be great if the entire jQuery was made up from components that do one thing each and could be used separately, all as plugins for a jQuery core that does nothing. – Timo Huovinen Dec 11 '14 at 19:41
  • 2
    @Timo Huovinen: Your question is really, really broad! When jQuery was created, it fitted a lot of cross browsers issues generated by browsers that are today less significant. Today, "javascript only" is easier than it was. At this time, creating a "big 20kb compressed, that contains all" was surely a good idea for so much reasons I prefer not to list them all. – dotpush Dec 13 '14 at 17:28
  • 2
    I don't like this. If people prefer this answer, ask yourself why you want to drop jQuery in the first place. It's a bit pointless if you're just going to extract the exact same functionality with all that browser fallback bloat back in to your bundle. Isn't that the whole point of avoiding jQuery in the first place? – Phil May 23 '17 at 14:54
  • @Phil_1984_ the answer exists to demonstrate what you just argued about, that the things that jQuery does are not that simple and provides a simple alternative. – Timo Huovinen May 25 '17 at 21:24
  • What does the second code snippet do, that the "viable replacement for jQuery ready" doesn't do? – Cave Johnson Feb 11 '19 at 18:57
  • @KodosJohnson Supposedly it's old browser stuff and runs when dynamically inserted – Timo Huovinen Feb 12 '19 at 06:42
  • @KodosJohnson I think a lot of that jQuery code is there to support 'Deferred', which was a way of doing async before we got Promises. – Stijn de Witt Feb 24 '21 at 00:53
214

Three options:

  1. If script is the last tag of the body, the DOM would be ready before script tag executes
  2. When the DOM is ready, "readyState" will change to "complete"
  3. Put everything under 'DOMContentLoaded' event listener

onreadystatechange

  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.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Jhankar Mahbub
  • 9,234
  • 9
  • 45
  • 50
  • 4
    This second example is much much more elegant and succinct than the marked answers. Why was this one not marked as the correct one? – 0112 Jul 16 '14 at 22:12
  • 3
    Still +1 for the DOMContentLoaded thing, it did exactly what I wanted. – tripleee Sep 16 '14 at 10:14
  • 2
    onreadystatechange did the trick for me ... needed to run some script after async jquery loading. – Abram Mar 10 '16 at 06:16
  • 4
    Just as an FYI, #1 is not entirely true. It's quite possible for a script at the end of the page to load before the DOM is done. That's why listeners are superior. They listen for when the browser is done. Putting it at the end is crossing your fingers that the script load was slower than the browser can render. – Machavity Oct 17 '16 at 14:11
  • For DOMContentLoaded listener, make sure to inline it in , and not in an async script – Drenai Aug 01 '17 at 19:17
  • 2
    this variant will also work when the document is already finished loading, please update your (imo best) answer if you can: if (document.readyState == 'complete') { init(); } else { document.onreadystatechange = function () { if (document.readyState == 'complete') { init(); } } } – ZPiDER Oct 17 '17 at 07:50
  • For DOM element interaction, there is also just: 'interactive' for ```Document.readyState``` https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState – CodeFinity Mar 26 '18 at 10:54
91

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...

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
rob
  • 9,413
  • 6
  • 39
  • 69
  • It seems to me that there were compatibility issues, like, since the page is not yet ready, you can't do this or that in these and those browsers. Unfortunately I cannot remember more clearly. Nonetheless +1 for a way that is close enough in 99% of all cases (and suggested by Yahoo!). – Boldewyn Dec 07 '09 at 16:49
  • 8
    Actually, putting a script element at the bottom of the page is an almost perfect solution. It works cross-browser and simulates document.ready perfect. The only disadvantage is that it's (a bit) more obtrusive than using some smart code, you will have to ask the user of the script you are creating to add an extra script fragment to call your ready or init function. – Stijn de Witt Mar 11 '11 at 22:12
  • @StijndeWitt - What do you mean about having to call an init function? A script that uses document.ready doesn't need other client code to call it, it is self-contained, and the equivalent to that where the code is included at the end of the body can also be self-contained and doesn't require other code to call it either. – nnnnnn Oct 21 '17 at 03:39
  • 1
    Why not put the script **after** the closing body tag, and before the closing ` – Charles Holbrow Apr 25 '18 at 19:27
  • 2
    @CharlesHolbrow Although all browsers will interpret it correctly, if you want it to be valid html, the [`html`](https://html.spec.whatwg.org/multipage/semantics.html#the-html-element) tag should only contain `head` and `body`. – Alvaro Montoro Dec 03 '18 at 22:56
  • 1
    @StijndeWitt, there is a quirky case when they differ. If you establish a [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) and don't wrap it with `document.ready`, it will catch everything between closing `` and closing `

    ` tags. Even if it is just a whitespace - in this case it will report a text node inserted. I've recently ran into this and was really surprised.

    – 1234ru Feb 22 '21 at 13:04
69

Poor man's solution:

var checkLoad = function() {   
    document.readyState !== "complete" ? setTimeout(checkLoad, 11) : alert("loaded!");   
};  

checkLoad();  

View Fiddle

Added this one, a bit better I guess, own scope, and non recursive

(function(){
    var tId = setInterval(function() {
        if (document.readyState == "complete") onComplete()
    }, 11);
    function onComplete(){
        clearInterval(tId);    
        alert("loaded!");    
    };
})()

View Fiddle

RevanthKrishnaKumar V.
  • 1,779
  • 1
  • 19
  • 33
Jakob Sternberg
  • 1,618
  • 14
  • 11
  • It might be worth limiting the recursions with an incrementing variable within the function. – Philip Langford May 19 '13 at 11:25
  • 9
    @PhilipLangford Or just put it inside of a `setInterval` and remove the recursion completely. – Alex W May 22 '13 at 17:40
  • does this work for anybody? I had to change `function(){ alert("loaded!")` to `(function(){ alert("loaded!"))()` – raveren Dec 09 '13 at 10:02
  • 1
    @Raveren , hmm you're right, i'm pretty sure i tested it when i posted it. anyways, it only became even simpler, now the function just get called, no wrapping. – Jakob Sternberg Dec 10 '13 at 03:42
  • I just came up with this one, it's also quite sexy: (function(){ document.readyState !== "complete" ? setTimeout(arguments.caller,11) : alert("Loaded!"); })() – Jakob Sternberg Jan 20 '14 at 17:09
  • @JakobSternberg any particular reason for choosing 11 milliseconds as the interval? – snapfractalpop Feb 18 '14 at 20:10
  • @JakobSternberg Anonymous recursion! Looks like you've invented a new Y combinator implementation in JS ;) – Cuadue Jun 19 '14 at 18:17
  • 27
    This is not sexy. No. Sorry. Using timers/intervals to detect stuff may "work" but if you keep programming like this any bigger project worth its salt is going to nose dive. Don't hack stuff together like this. Do it right. Please. This kind of code hurts the development ecosystem because there is a better solution and you KNOW it. – dudewad Jul 17 '14 at 21:35
  • 1
    I think this answer much closer to http://dustindiaz.com/smallest-domready-ever So I improved script: http://jsfiddle.net/iegik/PT7x9/ – iegik Jul 25 '14 at 13:07
  • @dudewad ... learning from mistakes is one of the best ways to learn. Saying "This kind of code hurts the development ecosystem" is a complete fallacy. In fact, it is more likely to improve it. You can't have a "right way" without a "wrong way" :) – reid Dec 04 '14 at 00:26
  • 1
    @ReidBlomquist Yes, and this is a "wrong" way, and that's what I'm pointing out (albeit a bit adamantly, I know). You could say that by doing it wrong it is somehow "helping" the ecosystem, but the problem is that with the amount of bad code out there that people take for "good" code because they don't have the experience to know any better does NOT help the ecosystem, because then they are going to take that bad code and implement it into an actual production architectural solution. So, I guess, we'll just have to differ in opinion on this "fallacy". – dudewad Dec 04 '14 at 18:20
  • @ReidBlomquist you have some backwards reasoning going on there – reformed Jan 16 '15 at 03:04
  • @AlexW, there are other reasons why this code is suboptimal, but the original's recursion is via a trampoline, so it's not problematic. – Bryan Larsen Nov 05 '15 at 03:30
38

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

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Dustin Davis
  • 14,049
  • 12
  • 60
  • 114
22

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();
        }
    }
},
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Dan
  • 48,995
  • 35
  • 110
  • 141
18

This question was asked quite a long time ago. For anyone just seeing this question, there is now a site called "you might not need jquery" which breaks down - by level of IE support required - all the functionality of jquery and provides some alternative, smaller libraries.

IE8 document ready script according to you might not need jquery

function ready(fn) {
    if (document.readyState != 'loading')
        fn();
    else if (document.addEventListener)
        document.addEventListener('DOMContentLoaded', fn);
    else
        document.attachEvent('onreadystatechange', function() {
            if (document.readyState != 'loading')
                fn();
        });
}
RevanthKrishnaKumar V.
  • 1,779
  • 1
  • 19
  • 33
chugadie
  • 2,572
  • 1
  • 20
  • 32
  • I wonder why the `'onreadystatechange'` is necessary rather than `document.attachEvent('onload', fn);` – Luke Oct 13 '15 at 21:31
14

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

Mikser
  • 609
  • 6
  • 15
13

I was recently using this for a mobile site. This is John Resig's simplified version from "Pro JavaScript Techniques". It depends on addEvent.

var ready = ( function () {
  function ready( f ) {
    if( ready.done ) return f();

    if( ready.timer ) {
      ready.ready.push(f);
    } else {
      addEvent( window, "load", isDOMReady );
      ready.ready = [ f ];
      ready.timer = setInterval(isDOMReady, 13);
    }
  };

  function isDOMReady() {
    if( ready.done ) return false;

    if( document && document.getElementsByTagName && document.getElementById && document.body ) {
      clearInterval( ready.timer );
      ready.timer = null;
      for( var i = 0; i < ready.ready.length; i++ ) {
        ready.ready[i]();
      }
      ready.ready = null;
      ready.done = true;
    }
  }

  return ready;
})();
AlienWebguy
  • 73,720
  • 16
  • 109
  • 137
James
  • 161
  • 1
  • 5
  • 14
    Be careful with this code. It's NOT equivalent to $(document).ready. This code triggers the callback when document.body is ready which doesn't guarantee the DOM is fully loaded. – Karolis Nov 09 '11 at 14:24
12

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);

Showing alert in jsfiddle

Pawel
  • 10,190
  • 4
  • 56
  • 60
11

The jQuery answer was pretty useful to me. With a little refactory it fitted my needs well. I hope it helps anybody else.

function onReady ( callback ){
    var addListener = document.addEventListener || document.attachEvent,
        removeListener =  document.removeEventListener || document.detachEvent
        eventName = document.addEventListener ? "DOMContentLoaded" : "onreadystatechange"

    addListener.call(document, eventName, function(){
        removeListener( eventName, arguments.callee, false )
        callback()
    }, false )
}
Miere
  • 1,259
  • 1
  • 16
  • 24
  • 1
    on some browsers, the `removeListener` will need to be called with document as the context, ie. `removeListener.call(document, ...` – Ron Mar 19 '13 at 03:44
9

Here is the smallest code snippet to test DOM ready which works across all browsers (even IE 8):

r(function(){
    alert('DOM Ready!');
});
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}

See this answer.

Community
  • 1
  • 1
Antara Roy
  • 437
  • 1
  • 6
  • 12
7

Just add this to the bottom of your HTML page...

<script>
    Your_Function();
</script>

Because, HTML documents are parsed by top-bottom.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
davefrassoni
  • 181
  • 1
  • 5
  • 7
    How do you know that DOM is built when this code is executed? Including loaded and parsed CSS? The browser API DOMContentLoaded is designed for that. – Dan Nov 07 '14 at 07:33
  • It really depends on what he wants to do with js. If he really needs to execute something when page has finished or not. – davefrassoni May 13 '19 at 01:54
6

It is worth looking in Rock Solid addEvent() and http://www.braksator.com/how-to-make-your-own-jquery.

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");});
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Ben
  • 1,841
  • 20
  • 24
5

Most minimal and 100% working

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.

Shivam Sharma
  • 655
  • 7
  • 20
4

This cross-browser code will call a function once the DOM is ready:

var domReady=function(func){
    var scriptText='('+func+')();';
    var scriptElement=document.createElement('script');
    scriptElement.innerText=scriptText;
    document.body.appendChild(scriptElement);
};

Here's how it works:

  1. The first line of domReady calls the toString method of the function to get a string representation of the function you pass in and wraps it in an expression that immediately calls the function.
  2. The rest of domReady creates a script element with the expression and appends it to the body of the document.
  3. The browser runs script tags appended to body after the DOM is ready.

For example, if you do this: domReady(function(){alert();});, the following will appended to the body element:

 <script>(function (){alert();})();</script>

Note that this works only for user-defined functions. The following won't work: domReady(alert);

Max Heiber
  • 10,336
  • 4
  • 47
  • 67
4

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);
    }
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Vatsal
  • 1,682
  • 15
  • 23
3

How about this solution?

// other onload attached earlier
window.onload=function() {
   alert('test');
};

tmpPreviousFunction=window.onload ? window.onload : null;

// our onload function
window.onload=function() {
   alert('another message');

   // execute previous one
   if (tmpPreviousFunction) tmpPreviousFunction();
};
mike
  • 69
  • 2
  • 3
    You could use addEventListener on window with "load". Listeners are executed one after one and dont need manually chaining. – Zaffy Jan 09 '13 at 10:36
  • 1
    But load is different than ready. The 'load' even happens before the document is 'ready'. A ready document has its DOM loaded, a loaded window doesn't necessarily have the DOM ready. Good answer though – Mzn Mar 30 '13 at 08:45
  • 1
    @Mzn: I think that's backwards. I think document ready happens *before* the window load event. "In general, it is not necessary to wait for all images to be fully loaded. If code can be executed earlier, it is usually best to place it in a handler sent to the .ready() method." (http://api.jquery.com/load-event/) – Tyler Rick Aug 23 '13 at 23:24
  • this will override rest of the window.onload events on the page and would cause issues. it should add event on top of existing one. – Teoman shipahi Feb 19 '14 at 23:08
  • The load event can happen too late. It is painful to use it when depending on third party external js/images... A non-responsive server you don't control and everything fail. Using DOMContentLoaded is not just an optimization, it's also safer! – dotpush Dec 10 '14 at 22:29
3

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);
};
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
malko
  • 2,137
  • 15
  • 23
2

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");
});
user4617883
  • 905
  • 1
  • 8
  • 17
2

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.

Dexygen
  • 11,681
  • 11
  • 73
  • 144
2

Comparison

Here (in below snippet) is comparison of chosen available browser "built-in" methods and their execution sequence. Remarks

  • the document.onload (X) is not supported by any modern browser (event is never fired)
  • if you use <body onload="bodyOnLoad()"> (F) and at the same time window.onload (E) then only first one will be executed (because it override second one)
  • event handler given in <body onload="..."> (F) is wrapped by additional onload function
  • document.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.
  • all scripts write their timestamp in console - but scripts which also have access to div write their timestamps also in body (click "Full Page" link after script execution to see it).
  • solutions readystatechange (C,D) are executed multiple times by browser but for different document states:
  • loading - the document is loading (no fired in snippet)
  • interactive - the document is parsed, fired before DOMContentLoaded
  • complete - the document and resources are loaded, fired before 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('<','&lt;').replace('>','&gt;') + '<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>
Kamil Kiełczewski
  • 53,729
  • 20
  • 259
  • 241
1

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.

Diego Perini
  • 7,197
  • 1
  • 16
  • 9
1

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:

  • fires immediately if the DOM is already ready (if the DOM is not "loading", but either "interactive" or "complete")
  • if the DOM is still loading, it sets up an event listener for when the DOM is available (interactive).

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.

Olemak
  • 1,585
  • 1
  • 13
  • 18
0

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.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Matt Pileggi
  • 6,688
  • 4
  • 14
  • 17
0

For IE9+:

function ready(fn) {
  if (document.readyState != 'loading'){
    fn();
  } else {
    document.addEventListener('DOMContentLoaded', fn);
  }
}
Joaquinglezsantos
  • 982
  • 10
  • 24
0
function onDocReady(fn){ 
    $d.readyState!=="loading" ? fn():document.addEventListener('DOMContentLoaded',fn);
}

function onWinLoad(fn){
    $d.readyState==="complete") ? fn(): window.addEventListener('load',fn);
} 

onDocReady provides a callback when the HTML dom is ready to fully access/parse/manipulate.

onWinLoad provides a callback when everything has loaded (images etc)

  • These functions can be called whenever you want.
  • Supports multiple "listeners".
  • Will work in any browser.
Jakob Sternberg
  • 1,618
  • 14
  • 11
0
(function(f){
  if(document.readyState != "loading") f();
  else document.addEventListener("DOMContentLoaded", f);
})(function(){
  console.log("The Document is ready");
});
Dustin Poissant
  • 2,710
  • 1
  • 17
  • 22
0

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!
});
Null
  • 659
  • 7
  • 10
0

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>
Kithraya
  • 290
  • 1
  • 9
0

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>
ceving
  • 16,775
  • 7
  • 82
  • 137
0

This should work:

document.addEventListener('DOMContentLoaded', function() {
 /* alert('Page loaded!');
 some code 
 more code */
});
-1

This was a good https://stackoverflow.com/a/11810957/185565 poor man's solution. One comment considered a counter to bail out in case of emergency. This is my modification.

function doTheMagic(counter) {
  alert("It worked on " + counter);
}

// wait for document ready then call handler function
var checkLoad = function(counter) {
  counter++;
  if (document.readyState != "complete" && counter<1000) {
    var fn = function() { checkLoad(counter); };
    setTimeout(fn,10);
  } else doTheMagic(counter);
};
checkLoad(0);
Community
  • 1
  • 1
Whome
  • 9,559
  • 6
  • 43
  • 59
-1

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();}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Forestrf
  • 364
  • 7
  • 12
-2

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!");
});
-2

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.

tnyfst
  • 1,575
  • 1
  • 9
  • 9
-4

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.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
  • 1
    Whenever a high-voted question appears on the front page, it's often worth checking the dates to make sure you're not responding to a very old posting. (One scenario I *might* respond is if over the years there is a new solution not present originally. DOMContentLoaded was definitely already mentioned though.) – Katana314 Jan 12 '16 at 16:45
-7

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
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
puchu
  • 2,801
  • 6
  • 30
  • 55