40

I try to optimize my pages by putting some async attributes on my scripts. It seems to break my javascript since $(document).ready is executed before the all scripts are loaded!

I saw that I can resolve my problem by putting $(window).load instead of $(document).ready but I was wondering if there is a better solution. This solution trigger 2 problems in my case :

  1. I have to change all $(document).ready and tell all the developpers to not use it anymore
  2. The scripts will be executed after all images are loaded. My website has a lot of heavy images and I really need some scripts to be executed ASAP after dom is ready.

Do you have some magic tricks? Maybe putting all scripts at the end? use defer instead of async?

tibo
  • 5,326
  • 4
  • 37
  • 53
  • document ready calls are executed also for scripts using document.ready after jQuery detected the document ready state. On a site where jQuery is used, try to use the following after all resources are loaded: ```$(document).ready(function () {console.log('READY');});``` – thet Dec 12 '18 at 06:58

4 Answers4

36

After some extensive research, I can definitely say that putting scripts at the end of the page is THE best practice.

Yahoo agrees with me : http://developer.yahoo.com/performance/rules.html#js_bottom

Google don't talk about this practice and seems to prefer async scripts : https://developers.google.com/speed/docs/best-practices/rtt#PreferAsyncResources

IMHO, putting script at the end of the page has several benefits over async/defer:

  • It will work for all browser (yes, even IE ;) )
  • You guarantee the execution order
  • You do not need to use $(document).ready or $(window).load
  • Your scripts can execute before your images are loaded
  • As async/defer, your page will be displayed quicker
  • When the DOM trigger the ready event, all scripts are loaded
  • Can be optimized by merging all js in one file without problem (by a tool like mod_pagespeed)

The only drawback that I can see is that the browser won't be able to parallelize the downloads. One good reason to use async/defer instead is when you have a script that is completly independant ( do not need to rely on the execution order) and that don't need to be executed at a specific timing. Example : google analytics.

tibo
  • 5,326
  • 4
  • 37
  • 53
  • 1
    It does not fully guarantee the execution by putting them down in the DOM. If you got too much JavaScript and / or HTML for the browser, you need requirejs or any other system loader like that. I saw design that worked only with compressed JavaScript, because it was too much. It was an bootstrap 3.x respsonsive theme with effects while scrolling down the page. – alpham8 Jun 20 '17 at 06:14
  • the only valuable argment to not use defer in 2012 was, as said by Yahoo : Firefox doesn't support the DEFER attribute. However now in 2020 you can use the defer mode every where : https://caniuse.com/#feat=script-defer – Reign.85 Jul 07 '20 at 07:27
  • See my answer below. Jquery has a built in call to help out with this exact issue. – Bonez024 Jun 22 '22 at 21:12
5

defer would definitely help here.

defer is generally better than async because it:

  • loads asynchronously (just like async)
  • guarantees the execution order (unlike async)
  • executes at the end (unlike async that executes in parallel when the page is still loading and it actually halts the dom-parsing!)
  • jquery ready fires after the "deferred" scripts have been loaded (this is what you're asking)

This SO answer has a very nice picture illustrating defer/async load order, super-understandable.

Alex from Jitbit
  • 53,710
  • 19
  • 160
  • 149
1

If you didn't want to use a script loader, you could use the following approach which would allow you to leave your $(document).ready scripts in place - modified as follows:

$(()=>{

    function checkAllDownloads() {
        // Ensure your namespace exists.
        window.mynamespace = window.mynamespace || {};

        // Have each of your scripts setup a variable in your namespace when the download has completed.
        // That way you can set async on all your scripts except jquery.
        // Use the document ready event - this code - to check if all your scripts have downloaded.
        if (window.mynamespace.script1 && window.mynamespace.script2){

          // Proceed with page initialisation now that all scripts have been downloaded.
          // [ Add your page initialisation code here ].
          return;
        } 
        // Not all downloads have completed.
        // Schedule another check to give the async downloads time to complete.
        setTimeout(checkAllDownloads, 500);
    }

    // check if it is safe to initialise the page by checking if all downloads have completed.
    checkAllDownloads();

    })
john blair
  • 466
  • 5
  • 13
0

I had this problem as well recently and resolved it using the built in "getScript" Call (jQuery)

Reference: https://api.jquery.com/jquery.getscript/

Example:

     $.getScript("[Your local file path or external URL]",
          function (data, textStatus, jqxhr) {
            
          //Code to run after the script has been loaded
        
      });
Bonez024
  • 1,365
  • 1
  • 13
  • 21