Back

Explore Courses Blog Tutorials Interview Questions
0 votes
2 views
in Web Technology by (20.3k points)

Okay, this might just be a silly question, though I'm sure there are plenty of other people asking the same question from time to time. Me, I just want to make 100% sure about it either way. With jQuery, we all know the wonderful

$('document').ready(function(){});

However, let's say I want to run a function that is written in standard JavaScript with no library backing it, and that I want to launch a function as soon as the page is ready to handle it. What's the proper way to approach this?

I know I can do:

window.onload="myFunction()";

...or I can use the body tag:

<body onload="myFunction()">

...or I can even try at the bottom of the page after everything, but the end body or HTML tag like:

<script type="text/javascript">

   myFunction();

</script>

What is a cross-browser(old/new)-compliant method of issuing one or more functions in a manner like jQuery's $.ready()?

1 Answer

0 votes
by (40.7k points)

The simple thing to do in the absence of a framework that does all the cross-browser compatibility for you is to just put a call to your code at the end of the body. 

That'll be faster to execute than an onload handler because this waits only for the DOM to be ready, not for all images to load.

And, this will work in every browser.

<html>

<head>

</head>

<body>

Your HTML here

<script>

// self executing function here

(function() {

   // your page initialization code here

   // the DOM will be available here

})();

</script>

</body>

</html>

But, if you don't want to do this way and you need cross-browser compatibility and don't want to wait for window.onload, then you should just understand how a framework like jQuery implements its $(document).ready() method. 

To give you a little idea what jQuery does (which will work wherever the script tag is placed). If supported, it tries the standard this way:

document.addEventListener('DOMContentLoaded', fn, false);

Try this with a fallback to:

window.addEventListener('load', fn, false )

Or, for older versions of IE, it uses the below code:

document.attachEvent("onreadystatechange", fn);

with a fallback to:

window.attachEvent("onload", fn);

Following is the full substitute for jQuery's .ready() written in plain javascript:

(function(funcName, baseObj) {

    // The public function name defaults to window.docReady

    // but you can pass in your own object and own function name and those will be used

    // if you want to put them in a different namespace

    funcName = funcName || "docReady";

    baseObj = baseObj || window;

    var readyList = [];

    var readyFired = false;

    var readyEventHandlersInstalled = false;

   // call this when the document is ready

    // this function protects itself against being called more than once

    function ready() {

        if (!readyFired) {

            // this must be set to true before we start calling callbacks

            readyFired = true;

            for (var i = 0; i < readyList.length; i++) {

                // if a callback here happens to add new ready handlers,

                // the docReady() function will see that it already fired

                // and will schedule the callback to run right after

                // this event loop finishes so all handlers will still execute

                // in order and no new ones will be added to the readyList

                // while we are processing the list

                readyList[i].fn.call(window, readyList[i].ctx);

            }

            // allow any closures held by these functions to free

            readyList = [];

        }

    }

    function readyStateChange() {

        if ( document.readyState === "complete" ) {

            ready();

        }

    }

    // This is the one public interface

    // docReady(fn, context);

    // the context argument is optional - if present, it will be passed

    // as an argument to the callback

    baseObj[funcName] = function(callback, context) {

        if (typeof callback !== "function") {

            throw new TypeError("callback for docReady(fn) must be a function");

        }

        // if ready has already fired, then just schedule the callback

        // to fire asynchronously, but right away

        if (readyFired) {

            setTimeout(function() {callback(context);}, 1);

            return;

        } else {

            // add the function and context to the list

            readyList.push({fn: callback, ctx: context});

        }

        // if document already ready to go, schedule the ready function to run

        if (document.readyState === "complete") {

            setTimeout(ready, 1);

        } else if (!readyEventHandlersInstalled) {

            // otherwise if we don't have event handlers installed, install them

            if (document.addEventListener) {

                // first choice is DOMContentLoaded event

                document.addEventListener("DOMContentLoaded", ready, false);

                // backup is window load event

                window.addEventListener("load", ready, false);

            } else {

                // must be IE

                document.attachEvent("onreadystatechange", readyStateChange);

                window.attachEvent("onload", ready);

            }

            readyEventHandlersInstalled = true;

        }

    }

})("docReady", window);

For the latest version of the code, you can refer to this link: https://github.com/jfriend00/docReady

Following are the Usage:

// This is used to pass a function reference

docReady(fn);

// it is uses as an anonymous function

docReady(function() {

    // code here

});

// pass a function reference and a context

// the context will be passed to the function as the first argument

docReady(fn, context);

// use an anonymous function with a context

docReady(function(context) {

    // code here that can use the context argument that was passed to docReady

}, ctx);

This has been tested in:

IE6 and up

Firefox 3.6 and up

Chrome 14 and up

Safari 5.1 and up

Opera 11.6 and up

Multiple iOS devices

Multiple Android devices

Working implementation and test bed: http://jsfiddle.net/jfriend00/YfD3C/

Let's understand how it works:

1. Create an IIFE (immediately invoked function expression) so that you'll have non-public state variables.

2. Try declaring a public function docReady(fn, context)

3. When docReady(fn, context) function is called, first check if the ready handler has already fired. 

4. If the ready handler is fired, then just schedule the newly added callback to fire right after this thread of JS finishes with setTimeout(fn, 1).

5. But, if the ready handler has not already fired, then add this new callback to the list of callbacks to be called later.

6. Now, check if the document is already ready. If so, execute all ready handlers.

7. But, If you haven't installed event listeners yet to know when the document becomes ready, then install them right now.

8. If document.addEventListener exists, then try to install event handlers using .addEventListener() for both "DOMContentLoaded" and "load" events. Here, the "load" is a backup event for safety and should not be needed.

9. If document.addEventListener doesn't exist, then try installing event handlers using .attachEvent() for "onreadystatechange" and "onload" events.

10. In the onreadystatechange event, check if the document.readyState === "complete" and if so, then call a function to fire all the ready handlers.

11. In all the other event handlers, call the function to fire all the ready handlers.

12. In the function to call all the ready handlers, check the state variable to see if you've already fired. If you haven't yet been called, then loop through the array of ready functions and call each one in the order they were added. Try setting a flag to indicate these have all been called so they are never executed more than once.

13. Now, clear the function array so any closures they might be using can be freed.

14. Handlers must be registered with docReady() are guaranteed to be fired in the order they were registered.

15. But, if you are trying to call docReady(fn) after the document is already ready, then the callback will be scheduled to execute as soon as the current thread of execution completes using setTimeout(fn, 1). This will allow the calling code to always assume they are async callbacks that will be called later, even if later the current thread of JS finishes and it preserves calling order.

Related questions

Browse Categories

...