{"id":5,"date":"2019-09-14T09:49:13","date_gmt":"2019-09-14T09:49:13","guid":{"rendered":"http:\/\/sumitjangid.com\/?p=5"},"modified":"2019-09-14T09:49:38","modified_gmt":"2019-09-14T09:49:38","slug":"async-and-deferred-javascript-introducing-script_loader_tag","status":"publish","type":"post","link":"http:\/\/sumitjangid.com\/index.php\/2019\/09\/14\/async-and-deferred-javascript-introducing-script_loader_tag\/","title":{"rendered":"Async and Deferred JavaScript: Introducing script_loader_tag"},"content":{"rendered":"\n<p>Today I\u2019m going to discuss a new, and very nice, site speed improvement that became possible in WordPress 4.1. What changed? The introduction of a new filter,&nbsp;<a href=\"https:\/\/developer.wordpress.org\/reference\/hooks\/script_loader_tag\/\"><code>script_loader_tag<\/code><\/a>. This filter lets us easily change the HTML markup of&nbsp;<code>enqueue<\/code>d&nbsp;<code>script<\/code>&nbsp;elements\u2014that is, of JavaScript files that were correctly added into a WordPress site using WordPress\u2019s&nbsp;<a href=\"https:\/\/wpshout.com\/everything-custom-scripts-styles-wordpress\/\"><code>wp_enqueue_script<\/code><\/a>&nbsp;function.<\/p>\n\n\n\n<p>With&nbsp;<code>script_loader_tag<\/code>, we can now easily fix a problem that can significantly impact page speed: lots of render-blocking JavaScript.<\/p>\n\n\n\n<h2>The Problem: Render-Blocking JavaScript<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>Long JavaScript files in your&nbsp;<code>head<\/code>&nbsp;can delay your browser from displaying page content, because its default behavior is first to interpret the JS files themselves.<\/p><\/blockquote>\n\n\n\n<p>Properly enqueued JavaScript shows up in the&nbsp;<code>head<\/code>&nbsp;section of your HTML document. On the internet as in nature, the main thing about a&nbsp;<code>head<\/code>&nbsp;is that it\u2019s above a&nbsp;<code>body<\/code>\u2014and this means something fairly serious for site speed, because JavaScript can be&nbsp;<em>render-blocking<\/em>.<\/p>\n\n\n\n<p>\u201cRender-blocking\u201d comes from a web browser\u2019s default behavior: It wants to completely receive and process everything that\u2019s come higher up in the page, before it moves any further down.<\/p>\n\n\n\n<p>This means that long JavaScript files in your&nbsp;<code>head<\/code>&nbsp;can actually delay your browser from displaying the page content in the&nbsp;<code>body<\/code>, because its default behavior is first to interpret the JS files themselves. In other words, JS is&nbsp;<em>blocking<\/em>&nbsp;the browser\u2019s crucial function of&nbsp;<em>rendering<\/em>&nbsp;the page out for the user to actually see. The result can be slow sites and frustrated users.<\/p>\n\n\n\n<p>Google\u2019s&nbsp;<a href=\"https:\/\/developers.google.com\/speed\/pagespeed\/insights\/\">Pagespeed Insights<\/a>&nbsp;has been pointing out this issue for a while:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/i0.wp.com\/wpshout.com\/media\/2015\/12\/blocking_js_before.png?ssl=1\"><img src=\"https:\/\/i0.wp.com\/wpshout.com\/media\/2015\/12\/blocking_js_before.png?resize=1024%2C741&amp;ssl=1\" alt=\"blocking_js_before\" class=\"wp-image-8824\"\/><\/a><\/figure>\n\n\n\n<p>Click to enlarge<\/p>\n\n\n\n<p>However, prior to 4.1 and&nbsp;<code>script_loader_tag<\/code>, the only solution I knew of was to move scripts to the site\u2019s footer. This is difficult to do by hand, and the plugins that claim to do it automatically didn\u2019t work in our case.<\/p>\n\n\n\n<p>Let\u2019s move straight to our success story:<\/p>\n\n\n\n<h2>The Goal: Much Less Render-Blocking JS<\/h2>\n\n\n\n<p>Here\u2019s what we got down to with the solution we\u2019ll present below:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/i0.wp.com\/wpshout.com\/media\/2015\/12\/blocking_js_after.png?ssl=1\"><img src=\"https:\/\/i0.wp.com\/wpshout.com\/media\/2015\/12\/blocking_js_after.png?resize=1024%2C666&amp;ssl=1\" alt=\"blocking_js_after\" class=\"wp-image-8823\"\/><\/a><\/figure>\n\n\n\n<p>Click to enlarge<\/p>\n\n\n\n<p>Now we\u2019ve only got three JavaScript files that can possibly slow down a page\u2019s rendering. The rest are still there, but they load&nbsp;<em>in parallel with<\/em>&nbsp;the page content, rather than&nbsp;<em>before<\/em>&nbsp;it.<\/p>\n\n\n\n<h2>The Fix: Defer and Async your JavaScript<\/h2>\n\n\n\n<p>The first thing to understand is the alternatives to render-blocking JS:&nbsp;<a href=\"http:\/\/www.w3schools.com\/tags\/att_script_defer.asp\"><code>defer<\/code><\/a>&nbsp;and&nbsp;<a href=\"http:\/\/www.w3schools.com\/tags\/att_script_async.asp\"><code>async<\/code><\/a>. We\u2019ll explain the difference, but both work similarly: They let the browser load a JS resource \u201cas time permits,\u201d while attending to other things (like page rendering) as well. This means that you&nbsp;<em>can\u2019t<\/em>&nbsp;rely on a deferred or asynced JavaScript file being in place prior to page render, as you could without these attributes\u2014but the advantage is that the file won\u2019t slow the speed at which the page becomes visible to users.<\/p>\n\n\n\n<p>Those are concepts\u2014now for code. (The full code is available&nbsp;<a href=\"https:\/\/github.com\/pressupinc\/wpshout-defer-scripts\">on GitHub<\/a>.)<\/p>\n\n\n\n<h3>1. GETTING YOUR SCRIPT HANDLES<\/h3>\n\n\n\n<p>Every properly enqueued WordPress script has a&nbsp;<em>handle<\/em>: a \u201cnickname\u201d that the site knows to call it by. We\u2019re going to need these handles for all scripts, and getting them isn\u2019t dead-simple, unfortunately.<\/p>\n\n\n\n<p>It is possible, though:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/*\t\n* Getting script tags\n* Thanks http:\/\/wordpress.stackexchange.com\/questions\/54064\/how-do-i-get-the-handle-for-all-enqueued-scripts\n*\/\n\nadd_action( 'wp_print_scripts', 'wsds_detect_enqueued_scripts' );\nfunction wsds_detect_enqueued_scripts() {\n\tglobal $wp_scripts;\n\tforeach( $wp_scripts->queue as $handle ) :\n\t\techo $handle . ' | ';\n\tendforeach;\n}<\/code><\/pre>\n\n\n\n<p>This code prints out a list of enqueued handles, separated by&nbsp;<code>|&nbsp;<\/code>, right into the&nbsp;<code>head<\/code>&nbsp;of every page:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img src=\"https:\/\/i0.wp.com\/wpshout.com\/media\/2015\/12\/list-of-handles.png?resize=576%2C319&amp;ssl=1\" alt=\"\" class=\"wp-image-8825\"\/><\/figure><\/div>\n\n\n\n<p>You\u2019ll only do this once, then use \u201cView Page Source\u201d to copy and paste the handles themselves.<\/p>\n\n\n\n<p>Once you\u2019ve done this, deactivate this section of the code: we\u2019ve got our handles, so let\u2019s not clog up our&nbsp;<code>head<\/code>&nbsp;with them anymore. That\u2019s why this section is commented out in the code on GitHub\u2014I don\u2019t want it to run every time!<\/p>\n\n\n\n<h3>2. DEFERRING AND ASYNCING RENDER-BLOCKING JAVASCRIPT<\/h3>\n\n\n\n<p>We found that we needed to use&nbsp;<a href=\"http:\/\/www.w3schools.com\/tags\/att_script_defer.asp\"><code>defer<\/code><\/a>&nbsp;and not&nbsp;<a href=\"http:\/\/www.w3schools.com\/tags\/att_script_async.asp\"><code>async<\/code><\/a>&nbsp;for WPShout, so I\u2019ll walk through the&nbsp;<code>defer<\/code>&nbsp;code. Most of the heavy lifting here is from&nbsp;<a href=\"http:\/\/scottnelle.com\/756\/async-defer-enqueued-wordpress-scripts\/\">an article by Scott Nelle<\/a>; thanks, Scott!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>add_filter( 'script_loader_tag', 'wsds_defer_scripts', 10, 3 );\nfunction wsds_defer_scripts( $tag, $handle, $src ) {\n\n\t\/\/ The handles of the enqueued scripts we want to defer\n\t$defer_scripts = array( \n\t\t'prismjs',\n\t\t'admin-bar',\n\t\t'et_monarch-ouibounce',\n\t\t'et_monarch-custom-js',\n\t\t'wpshout-js-cookie-demo',\n\t\t'cookie',\n\t\t'wpshout-no-broken-image',\n\t\t'goodbye-captcha-public-script',\n\t\t'devicepx',\n\t\t'search-box-value',\n\t\t'page-min-height',\n\t\t'kamn-js-widget-easy-twitter-feed-widget',\n\t\t'__ytprefs__',\n\t\t'__ytprefsfitvids__',\n\t\t'jquery-migrate',\n\t\t'icegram',\n\t\t'disqus',\n\t);\n\n    if ( in_array( $handle, $defer_scripts ) ) {\n        return '&lt;script src=\"' . $src . '\" defer=\"defer\" type=\"text\/javascript\">&lt;\/script>' . \"\\n\";\n    }\n    \n    return $tag;\n} <\/code><\/pre>\n\n\n\n<p>The&nbsp;<code>add_filter<\/code>&nbsp;line tells us that this code should run anytime an enqueued JavaScript file is about to be printed onto the page as an HTML&nbsp;<code>script<\/code>&nbsp;element. Letting us filter that HTML is what&nbsp;<code>script_loader_tag<\/code>&nbsp;is for. (If you need an update on filters and WordPress\u2019s hooks system in general,&nbsp;<a href=\"https:\/\/wpshout.com\/wordpress-hooks-actions-filters-work\/\">start here!<\/a>)<\/p>\n\n\n\n<p>The biggest check in this code is doing a single thing defining the&nbsp;<code>$defer_scripts<\/code>&nbsp;array. This array lists out the handles of all the elements we want to defer\u2014the handles we found in step 1. (Your handles will, of course, vary!)<\/p>\n\n\n\n<p>The logic below the array definition (beginning with&nbsp;<code>if ( in_array(&nbsp;<\/code>) searches for the current script\u2019s handle attribute in the array we\u2019ve just defined. If the handle matches an element in the array, then we modify the script to have the same source, but with a new property:&nbsp;<code>defer=\"defer\"<\/code>, which will cause the script not to block rendering. With this change made, we&nbsp;<code>return<\/code>&nbsp;the HTML back, and we\u2019re good to go!<\/p>\n\n\n\n<p>(And finally, if the handle isn\u2019t found, we just return the original tag itself, unaltered.)<\/p>\n\n\n\n<p>You\u2019ll know this plugin is working when you view page source and see something like this in the&nbsp;<code>head<\/code>:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img src=\"https:\/\/i0.wp.com\/wpshout.com\/media\/2015\/12\/deferred_scripts.png?resize=1024%2C465&amp;ssl=1\" alt=\"deferred_scripts\" class=\"wp-image-8829\"\/><\/figure><\/div>\n\n\n\n<h4>When to Use Async Instead of Defer<\/h4>\n\n\n\n<p>You use&nbsp;<a href=\"http:\/\/www.w3schools.com\/tags\/att_script_async.asp\"><code>async<\/code><\/a>&nbsp;when you\u2019re linking directly to an&nbsp;<em>external<\/em>&nbsp;JavaScript library. That link would look something like:&nbsp;<code>&lt;script src=\"https:\/\/oss.maxcdn.com\/libs\/respond.js\/1.4.2\/respond.min.js\"&gt;&lt;\/script&gt;<\/code>. Notice how it\u2019s a link to the full URL, and the JavaScript will get pulled in<\/p>\n\n\n\n<p><code>enqueue<\/code>ing&nbsp;<em>external<\/em>&nbsp;JS is a lot less common, at least for us, since most of our enqueued JS is in themes and plugins that host their own code. At any rate, the code for&nbsp;<code>async<\/code>&nbsp;is precisely the same as the code for&nbsp;<code>defer<\/code>\u2014but with the two words switched out. So if you do happen to have a lot of externally hosted&nbsp;<code>enqueue<\/code>d scripts, getting them&nbsp;<code>async<\/code>ed&nbsp;is a very similar technical process to the one we\u2019ve just covered.<\/p>\n\n\n\n<h3>WHICH SCRIPTS TO DEFER AND ASYNC<\/h3>\n\n\n\n<p>You\u2019ll notice that we didn\u2019t defer everything\u2014a few scripts are still render-blocking. Here are rules of thumb on that:<\/p>\n\n\n\n<ul><li>Don\u2019t do anything to jQuery. jQuery (handle&nbsp;<code>jQuery<\/code>) is a key dependency for many other JS files, and you want to let it load early.<\/li><li>Any file that\u2019s wrapped in a&nbsp;<code>jQuery( document ).ready( function() { })<\/code>&nbsp;call should be fine to defer. That code basically says \u201cWait until the entire document object model (DOM) loads,\u201d so racing to get the JavaScript file loaded&nbsp;in the head&nbsp;doesn\u2019t serve much purpose.<\/li><li>In general, you can defer JavaScript files&nbsp;that rely on user interactions, like clicks and mouse hovers\u2014and files that fix layout details, like center or hide a set of element. Again, these rely on a loaded page to work anyway (which is why they\u2019re almost all going to be wrapped in&nbsp;<code>jQuery( document ).ready( function() { })<\/code>, or else they\u2019re liable not to work), so you should be safe to get the page out beforehand.<\/li><li>It\u2019s, unfortunately, impossible to use this method for JavaScript files that have been added some way other than the generally correct method of&nbsp;<code>enqueue<\/code>ing them. This is another reason to&nbsp;<a href=\"https:\/\/wpshout.com\/everything-custom-scripts-styles-wordpress\/\">prefer that method<\/a>&nbsp;over other ways of loading scripts that may appear to work fine at first glance.<\/li><\/ul>\n\n\n\n<h2>Summing Up<\/h2>\n\n\n\n<p>This turned into a longish post, but at its core is a very cool and rather quick way to improve your site speed and user satisfaction. I hope you now know enough to defer and async your own JavaScript files, and thanks for reading! I\u2019d love to hear comments or questions below.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today I\u2019m going to discuss a new, and very nice, site speed improvement that became possible in WordPress 4.1. What changed? The introduction of a new filter,&nbsp;script_loader_tag. This filter lets us easily change the HTML&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[8,7],"tags":[],"_links":{"self":[{"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/posts\/5"}],"collection":[{"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/comments?post=5"}],"version-history":[{"count":1,"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/posts\/5\/revisions"}],"predecessor-version":[{"id":6,"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/posts\/5\/revisions\/6"}],"wp:attachment":[{"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/media?parent=5"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/categories?post=5"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/sumitjangid.com\/index.php\/wp-json\/wp\/v2\/tags?post=5"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}