Is there any equivalent of "$(Document). ready()" with pure Javascript?

Asked

Viewed 5,760 times

12

In jQuery, we need to use $(document).ready() to know if the document has been loaded and then safely run our jQuery script.

According to jQuery documentation

A page can’t be manipulated Safely until the Document is "ready." jQuery detects this state of Readiness for you.

Translation:

The page cannot be handled safely until the document is "ready". jQuery detects that state of readiness for you.

Now, in the case of pure Javascript, without jQuery or any other framework, there is some way to detect that the document has been loaded, to have the same guarantee that we have with $(document).ready()?

3 answers

13


What the jQuery is to read the property document.readyState when the page loads, and if it is not already loaded, listen to one of the events load or DOMContentLoad. The first event to be called triggers the ready.

In practice simplifying would be so (jsFiddle):

function ready() {
    // quando esta função correr o DOM está acessível
}

function completed() {
    document.removeEventListener("DOMContentLoaded", completed);
    window.removeEventListener("load", completed);
    ready();
}

if (document.readyState === "complete" ||
    (document.readyState !== "loading" && !document.documentElement.doScroll)) {
    ready(); // está pronto!
} else { // ainda não está pronto...
    document.addEventListener("DOMContentLoaded", completed);
    window.addEventListener("load", completed);
}

IE 8 had a bug with scroll and so this line that refers to Scroll. There’s a very good article about that (link), but since IE8 is no longer supported you can remove.

To version of Mootools is similar, a little more complex and therefore more complete, and uses the internal mechanism of Mootools events. Also take into account older browsers that do not have readystatechange. But that no longer matters in today’s times.


For modern browsers this can be done (link):

var domReady = function(ready) {
    if (document.readyState != 'loading') return ready();
    document.addEventListener('DOMContentLoaded', ready);
    function _ready() {
        document.removeEventListener('DOMContentLoaded', ready);
        ready();
    }
}

For old browsers this could be done (link), keeping it simple:

var domReady = function(ready) {
    var attacher = document.addEventListener ? {
        add: 'addEventListener',
        remove: 'removeEventListener'
    } : {
        add: 'attachEvent',
        remove: 'detachEvent'
    };

    function completed() {
        document[attacher.remove]("DOMContentLoaded", completed);
        window[attacher.remove]("load", completed);
        ready();
    }

    if (document.readyState === "complete" ||
        (document.readyState !== "loading" && !document.documentElement.doScroll)) {
        ready(); // está pronto!
    } else { // ainda não está pronto...
        document[attacher.add]("DOMContentLoaded", completed);
        window[attacher.add]("load", completed);
    }
}
  • 1

    Some versions below IE 9 will also not work with addEventListener, so you can replace the method with attachEvent if there is one (a note in case someone cares about browser support).

  • 1

    @Klaiderklai true, stay here (link) jQuery version for older browsers, taking into account this aspect. Another possibility would be to use window.onload = function(){ready();} for my example above, but also for very old browsers, so not next to the answer, it is here.

  • 1

    @Klaiderklai is right, a polyfill would be fine

  • 1

    @Guilhermenascimento joined a version for old browsers.

  • @Guillhermenascimento força.

  • 1

    Now that I realize, Document.readyState only has 3 possible values, and I using regex for no reason (/^(interactive|complete)$/). Living and learning :D

  • @Sergio just for the record, although today no longer make differences in modern browsers, the doScroll is a function and not a property, I wanted to update your code, but the implementation of it requires Try/catch, so I prefer not to modify your logic, but if you can adjust it gets better. Thank you.

  • @Exact Guilhermenascimento. Hence the code have ! to convert to a boolean if that method is present in the documentElement, without invoking him.

  • I think I get it, so the intention is that in IE only run via load and support via Domcontentloaded?

  • @Guilhermenascimento in IE8 (which implies not having .doScroll) anything other than loading is a page lacking.

  • @Sergio understand, thank you.

Show 6 more comments

6

To look similar to jQuery’s operation we have to add an extra random check.

  • Note that interactive does not work well in IE so I used doScroll
  • Note that document.readyState not supported by some browsers, so I kept onload as a "fallback"
  • To use document.readyState it is necessary setInterval or setTimeout
  • The variable isReady serves to avoid running a script every chance is already loaded (also avoiding problems in browsers without support for document.readyState)
  • The (function () { serves to isolate the variable isReady staying within the scope
  • The event DOMContentLoaded is triggered when the initial HTML document has been fully loaded and parsed, without waiting for style sheets, images, and subframes to finish loading, but is not supported by some older browsers such as IE8
  • In the script below the DOMContentLoaded runs parallel with the other functions or whoever finishes first will fire the callback.
  • The event load in the window it is a warranty that everything fails or is a browser without any support (it is quite rare)

The code would look like this:

(function (w, d) {
    var isReady = false;

    w.domReady = function(callback)
    {
        /*
        Se a página já estiver carregada e já tiver rodado alguma vez
        o window.domReay então esta if evita fazer registro de eventos
        ou testes desnecessários, assim otimizando o tempo de resposta
        */
        if (isReady) {
             callback();
             return;
        }

        var done = false;
    
        var doc = d.documentElement;
    
        var attacher = d.addEventListener ? {
            add: 'addEventListener',
            remove: 'removeEventListener'
        } : {
            add: 'attachEvent',
            remove: 'detachEvent'
        };
    
        function completed() {

            /*
            A variável done impede que o script seja executado duas vezes
            */
            if (done) { return; }

            done = true;
            isReady = true;
    
            d[attacher.remove]("DOMContentLoaded", completed);
            w[attacher.remove]("load", completed);
            callback();
        };
    
        if (d.readyState === "complete") {
            /*Se o navegador suportar readyState e a página já estiver carregada*/
            completed();
        } else if (doc.doScroll) {

            /*interactive para IE8 não funciona bem, doScroll é um fallback para IE8*/

            (function doScrollTest() {
                try {
                    doc.doScroll('left');
                    completed();
                } catch(e) {
                    setTimeout(doScrollTest, 50);
                }
            })();
        } else if (d.readyState) {

            /*Checa se é interactive ou completed*/

            (function readyStateTest() {
                if (d.readyState !== "loading") {
                    completed();
                } else {
                    setTimeout(readyStateTest, 50);
                }
            })();
        }
    
        d[attacher.add]("DOMContentLoaded", completed);
        w[attacher.add]("load", completed);
    };
})(window, document);

//Usando:
domReady(function() {
    console.log("ready");
});

domReady(function() {
    console.log("olá mundo");
});

domReady(function() {
    domReady(function() {
        console.log("domReady dentro de domReady");
    });
});

window.onload = function () {
    domReady(function() {
       console.log("domReady dentro de Onload");
    });
};

  • 2

    Good, +1, for old browsers gets here a good version too.

3

You can also use the script tags at the end of body, as shown in the code below. With the script inside body the code will only be executed when body is loaded.

<!DOCTYPE html>
<html lang="pt-br">
<head>
    <title></title>
</head>
<body>
    <script>
        alert('A página foi carregada!');
    </script>
</body>
</html>
  • 1

    I think that window.onload is equivalent to $(window).load(), not to the ready.

  • Apart from the "window.onload part, as shown above is the equivalent of $(Document). ready() jQuery" that I disagree, the rest of the tip this excellent André, yes put the script before the </body> and if you put everything inside a single . js you can still use async which helps speed up rendering. Congratulations, just remove the snippet I mentioned.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.