jQuery $(document).ready() Equivalent in Vanilla JavaScript

May 17th, 2015
Twitter Facebook Google+

When you have JavaScript code that interacts with elements on a page, in order to avoid any error, you must wait for these elements to be added to the DOM. You can always put your JavaScript code at the tail end of the body element, but what if you are unable to control where your code is placed? This is where a lot of people will turn to jQuery.

jQuery provides a very useful ready() function for determining when the DOM has finished loading. The DOM will be considered loaded once the browser has finished traversing and parsing the HTML and constructs it. By waiting for the DOM, you can be sure your JavaScript code will have access to all page elements. Page assets that can take much longer to load, such as images, are not waited for. You would implement this function as shown below.

$(document).ready(function() {
    // Your code here
});

Or, using the shorthand version

$(function() {
    // Your code here
});

If you were to use the window.onload event instead, this would wait for all assets to load before executing your code, including all images, stylesheets, and other scripts.

Including jQuery just to detect when the DOM has loaded is a bit overkill, though. The same can be achieved without jQuery using just vanilla JavaScript. I'll show you two ways of doing it depending on which browsers you want to support.

DOM Ready for Internet Explorer 9 and Above

Since Internet Explorer 9 supports both the addEventListener() method and DOMContentLoaded event, detecting when the DOM has loaded is very easy. So easy in fact, that it's pretty much just one line of code.

var domReady = function(callback) {
    document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback);
};

We can use this domReady() function just like the jQuery ready() function.

domReady(function() {
    // Your code here
});

The reason we check the value of document.readyState is to ensure that our code will be executed if the DOM has already loaded. If we were to only use the DOMContentLoaded event, and our code is in a JavaScript file that finishes loading after the DOM has loaded, it will never run.

A bug posted to the jQuery site (http://bugs.jquery.com/ticket/12282#comment:15) shows that under certain circumstances, Internet Explorer 9 will switch the readyState to interactive when the DOM hasn't truly finished loading, which may cause unwanted effects. This bug though from what I can tell is very rare and should not affect the vast majority of use cases.

DOM Ready for Internet Explorer 8 and Below

This method, while not nearly as simple as the first, provides the highest level of compatibility with browsers. It's based on the source code to version 1.11.3 of jQuery.

var domReady = function(callback) {
    var ready = false;

    var detach = function() {
        if(document.addEventListener) {
            document.removeEventListener("DOMContentLoaded", completed);
            window.removeEventListener("load", completed);
        } else {
            document.detachEvent("onreadystatechange", completed);
            window.detachEvent("onload", completed);
        }
    }
    var completed = function() {
        if(!ready && (document.addEventListener || event.type === "load" || document.readyState === "complete")) {
            ready = true;
            detach();
            callback();
        }
    };

    if(document.readyState === "complete") {
        callback();
    } else if(document.addEventListener) {
        document.addEventListener("DOMContentLoaded", completed);
        window.addEventListener("load", completed);
    } else {
        document.attachEvent("onreadystatechange", completed);
        window.attachEvent("onload", completed);

        var top = false;

        try {
            top = window.frameElement == null && document.documentElement;
        } catch(e) {}

        if(top && top.doScroll) {
            (function scrollCheck() {
                if(ready) return;

                try {
                    top.doScroll("left");
                } catch(e) {
                    return setTimeout(scrollCheck, 50);
                }

                ready = true;
                detach();
                callback();
            })();
        }
    }
};

Just a bit more in depth than the first one, right? It basically does the same thing as the first method, just with fallback support for the Internet Explorer event model and some quirks.

We can use it just like the first method.

domReady(function() {
    // Your code here
});

And it supports the following browsers.

IE6+
Firefox 3.6+
Chrome
Safari 5.1+
Opera 11.6+
Android

Hope this helps someone out there, leave any questions or feedback in the comments section below!

Comments