Non-blocking Javascript Patterns

698032ac4563906c45099613cb6b80a7?s=47 George White
May 01, 2012

Non-blocking Javascript Patterns


George White

May 01, 2012


  1. Non-blocking Javascript Patterns Tuesday, May 1, 12

  2. @stonehippo S Tuesday, May 1, 12

  3. Blocking Javascript: what’s the issue exactly? Tuesday, May 1, 12

  4. Web browsers handle all activities in a page in single

    thread. This includes the downloading* and execution of Javascript. * Modern browsers download multiple resources at the same time, offsetting this issue somewhat. Tuesday, May 1, 12
  5. Therefore, the loading and execution of Javascript while a page

    is loading can block page rendering, leading to poor perceptual performance. Tuesday, May 1, 12
  6. <!DOCTYPE html> <html> <head> <script> // Code here will block

    page rendering while executing </script> </head> <body> <h1>Welcome to my super-awesome page!</h1> <script> // Any inline code will also block page rendering </script> <!-- Mind-blowing content here --> … </body> </html> Inline code inserted in the head or body of the page will block the loading of assets that follow, and the rendering of the page, until the script has fully loaded and executed. Tuesday, May 1, 12
  7. <!DOCTYPE html> <html> <head> <!-- Loading the external script will

    block asset loading and page rendering until it is done --> <script src=“my_special_scripts.js”></script> </head> <body> <h1>Welcome to my super-awesome page!</h1> <!-- Mind-blowing content here --> … </body> </html> External scripts have the same issue. In fact, they can be worse, as they require an extra HTTP request to load. Tuesday, May 1, 12
  8. When Javasript loading and execution blocks the page, everything else

    grinds to a halt. If you have a lot of Javascript executing in the <head> or inline, the page will be perceived as slow. And slow is BAD.* * Tuesday, May 1, 12
  9. Alright, so what can we do about this? Tuesday, May

    1, 12
  10. Late script loading Tuesday, May 1, 12

  11. Placing all script elements at the bottom of the page—just

    above the closing </body> tag–is a simple method for improving perceived page loading performance. Tuesday, May 1, 12
  12. <!DOCTYPE html> <html> <body> <h1>Welcome to my super-awesome page!</h1> <!--

    Mind-blowing content here --> … <!-- This script will block, but it’s less of an issue because the DOM has already been loaded --> <script src=“my_special_scripts.js”></script> </body> </html> This technique doesn’t really prevent Javascript from blocking; it just defers the loading and execution until the rest of the page has loaded . Tuesday, May 1, 12
  13. The createElement-insertBefore script insertion pattern Tuesday, May 1, 12

  14. In this pattern, scripts are load dynamically via a <script>

    element created via the createElement() DOM method and inserted into the page DOM via insertBefore(). This is the method used by Google Analytics to load its tracking code without blocking the page. Tuesday, May 1, 12
  15. <!DOCTYPE html> <html> <head> <!-- Insert a script that won’t

    block --> <script> var js = document.createElement(“script”), base = document.getElementsByTagName(“script”)[0] js.async = true js.src = "script.js” base.parentNode.insertBefore(js, base) </script> </head> <body> <h1>Welcome to my super-awesome page!</h1> … </body> </html> In this example, script.js is loaded asynchronously via an inject script element. Note that the injection script will block! Tuesday, May 1, 12
  16. Load scripts via a script loader Tuesday, May 1, 12

  17. Script loaders are Javascript libraries that handle the loading and

    execution of scripts in a non-blocking fashion. Most loaders offer both asynchronous loading and control of execution, plus callbacks to fire off code based on loaded scripts. Tuesday, May 1, 12
  18. <!DOCTYPE html> <html> <head> <!-- Insert a script that won’t

    block --> <script src=“head.js”></script> <script> // fire off some code when all scripts are loaded head.ready(function() { // Note the call to jQuery, which isn’t loaded. Cool. $(“#element”).show(); }) // Load jQuery asychronously head.js(“/path/to/jquery.js”) </script> </head> <body> … Loading an external script with head.js is pretty simple. Note the we can call unloaded library code with head.ready(). Tuesday, May 1, 12
  19. There are many different script loaders available. A few popular

    loaders: • head.js • LABjs • yepnope.js (included with Modernizr) • ControlJS • $scripts.js There are many more out there. Tuesday, May 1, 12
  20. Note that speed and execution control vary across the script

    loader implementations. Not every loader is ideal for all situations. For a comparison of load speeds of various loaders, check out Tuesday, May 1, 12
  21. Non-blocking HTML5: the async and defer attributes Tuesday, May 1,

  22. HTML5 standardized the async and defer attributes for the <script>

    element. This attribute has been around for a while for IE, and was added to FireFox a while ago. Support is good across modern browsers*. External scripts† using either of these will be loaded and execute in a non-blocking manner. * | defer † Scripts loaded with the src attribute. Tuesday, May 1, 12
  23. The defer attribute tells the browser that it can load

    the script asynchronously and execute is after the DOM is ready. Execution order is determined by source order. <!DOCTYPE html> <html> <head> <!-- This script won’t block and will be executed when the DOM is ready --> <script src=“jquery.min.js” defer></script> </head> <body> <h1>Welcome to my super-awesome page!</h1> <!-- This won’t block either, and it will be executed after after the other script --> <script src=“script.js” defer></script> <!-- Mind-blowing content here --> … </body> </html> Tuesday, May 1, 12
  24. The async attribute informs the browser that it can download

    the script asynchronously, and execute it whenever the loading is done, regardless of source order. <!DOCTYPE html> <html> <head> <!-- This script won’t block and will be executed as soon as it finishes loading --> <script src=“jquery.min.js” async></script> </head> <body> <h1>Welcome to my super-awesome page!</h1> <!-- This won’t block either, and it will be executed when it loads, too --> <script src=“script.js” async></script> <!-- Mind-blowing content here --> … </body> </html> Tuesday, May 1, 12
  25. Comparing Non-blocking Methods Tuesday, May 1, 12

  26. Loading scripts at the end of the page is simple,

    and works everywhere. But it also means that you have to gang up all of your Javascript elements at the end of the page. And each script will block the ones that follow it. Tuesday, May 1, 12
  27. Using the createElement-Pattern is simple and does the trick if

    you’re loading a single library. But this doesn’t really scale if you’re loading a lot of other Javascript. You may want to consider a script loader in that case. Tuesday, May 1, 12
  28. Script loaders are flexible and work just about everywhere. They

    also add to the execution times and overall weight of your pages. And they can have quirky behaviors. Also note that most of these will cause other scripts to load after the DOM ready event. So use a library that handles this case (e.g., jQuery). Tuesday, May 1, 12
  29. Using async and defer is fast, native to the browser,

    and provides some control over script execution order. And they can be mixed with other techniques, such as script loaders. This method doesn’t provide any clear way to fire callbacks. And older browsers don’t support it, or have inconsistent support. But to be clear: async and defer are the future! Tuesday, May 1, 12
  30. Non-blocking loading is only one aspect of Javascript performance. Some

    other things to consider: • Less is always better; do you really need jQuery? • Cache everything you can; avoid HTTP requests whenever possible • Avoid touching the DOM • Learn more about Javascript! And check out other optimizations, too. Tuesday, May 1, 12
  31. George White @stonehippo S With a hat tip to

    Steve Souders! Check out the typefaces • Chunk • Pictos •Veneer Extras Tuesday, May 1, 12