WordPress provides many hooks into their API. These hooks are defined either as Actions or Filters. WordPress defines various hooks within API functions that allow programmers to intercept the output of a function and alter it before the API function returns that output. For obvious reasons, this type of hook is called a Filter.
In this article, we will intercept the generation of <script> tags, and alter the output in an attempt to improve web site performance. The filter code will add either a defer or async attribute to the <script> tag.
The defer attribute delays the execution of a script until the web page has finished parsing. This example adds the defer attribute to scripts that load from the same web site as the web page.
The async attribute begins execution of a script immediately in parallel with the rest of the web page as it loads. This example adds the async attribute to scripts that load from external web sites, such as google.com.
This code will also allow the programmer to define a list of exceptions. Any script filename with a partial match found in the exception list is unmodified; a filter hook function does not always have to modify the received data.
Place the following code example in the current WordPress theme’s functions.php file for automatic execution.
A WordPress Lazy Script Loader
The Filter hook we will use is called ‘script_loader_tag’. This filter is called when WordPress is generating <script> tags. An example would be when the wp_enqueue_script( ) is called in a theme’s functions. The lazy_scripts( ) function is registered as a Filter hook callback function with WordPress in the call to add_filter( ).
- Define $domain variable as static to keep its value between calls. This is a minor performance improvement. It ensures that for each web request, the function only has to calculate the web site’s domain name once.
- If the $url variable does not contain a sub-string of ‘.js’ the function returns without modifying the tag. There are some cases where the value of the src attribute might not be a file with a js extension, such as dynamically generated scripts, or script tags containing JSON-LD content.
- If the $tag variable (the <script> tag generated by WordPress) already contains a sub-string of ‘ defer’ or ‘ async’ then the function returns without modifying the tag. There may be some plugins or themes that already specify one of these attributes when loading scripts.
- If a script is locally embedded in the web page content, or the theme template has hard-coded <script> tags (instead of using wp_enqueue_script( ) calls) then this function will not be called for those <script> tags.
- The add_filter( ) call has a priority parameter of PHP_INT_MAX. This is to try to execute this filter function after any other filters registered for this same hook.
- You can try testing your web site with a free performance tester like gtmetrix.com with and without this filter enabled to see if your speed score improves. To do this, simply comment out the last line:
// add_filter( ‘script_loader_tag’, ‘lazy_scripts’, PHP_INT_MAX, 3 );