javascriptjqueryjquery-deferreddocument-ready

Explain why the order of operations is different between jQuery 2.2.4 and jQuery 3.7.1


Given three files:

  1. view.html:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Boilerplate HTML Page</title>
        <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
        <script src="helper.js" defer></script> <!-- Include helper.js -->
    </head>
    <body>
        <header>
            <h1>Welcome to My Page</h1>
        </header>
        <main>
            <p>This is a simple boilerplate HTML page.</p>
        </main>
        <footer>
            <p>&copy; 2024 Your Name</p>
        </footer>
    </body>
    </html>
  1. helper.js:
    // document.addEventListener("DOMContentLoaded", function() {
    $(document).ready(function () {
        var initPicker = function () {
            console.log("helper");
        };

        var init = function () {
            $.getScript("picker.js")
                .done(function () {
                    initPicker();
                })
                .fail(function (ex) {
                    console.error("Failed to load the picker script.");
                });
        };

        init();
    });
  1. picker.js:
    var cp = cp || {};

    // document.addEventListener("DOMContentLoaded", function() {
    $(document).ready(function () {
        cp.picker = (function($) {
            console.log("picker");
        })($);
    });

Fire this up with node or whatever you use, and open js console logs and refresh the page.You will see that "picker" is logged first, and "helper" second. This is the desired case for me.

However, if you change jquery from 2.2.4 to 3.7.1 (and no other changes), then you will see "helper" logged first and "picker" logged second.

Furthermore, replacing the two calls to $(document).ready with vanilla javascript's document.addEventListener("DOMContentLoaded"), will result in only "helper" getting logged and "picker" is not logged at all.

I've tried a number of different things such as using promises, changing defer and async values, etc. Nothing seems to work unless:

  1. You change jquery version back to 2.2.4
  2. You actually import before within the view.html header.
  3. You remove the $(document).ready that wraps the "picker.js" file.

Unfortunately, in my particular case none of those three options are viable. I need to understand exactly what is going on here so that I can decide on a course of action or find another solution. I am struggling to find answers on the interwebs.

I need to find out how to update this in such a way that both "picker.js" and "helper.js" are loaded after the DOM is ready, and "picker.js" loads before "helper.js", with minimal changes - and not one of the three listed options above.


Solution

  • This would be jQuery 3.0’s Breaking change: document-ready handlers are now asynchronous.

    As part of jQuery 3.0's alignment with the Promises/A+ standard, document-ready handlers are called asynchronously even if the document is currently ready at the point where the handler is added.


    You remove the $(document).ready that wraps the "picker.js" file.

    That or something close to it really sounds like the correct option, though you haven’t explained why you can’t do this so it’s hard to suggest alternatives.

    One hack that only modifies helper.js is to enqueue a redundant ready handler:

    var initPicker = function () {
        $(document).ready(function () {
            console.log("helper");
        });
    };