Last active
August 9, 2023 21:00
-
-
Save adamsilverstein/ec18b67a72ff74dec12624e989e23142 to your computer and use it in GitHub Desktop.
WordPress `wp_enqueue_script`: use `async` or `defer` in a backwards compatible manner
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Register scripts with a `defer` or `async` strategy in a backwards compatible manner. | |
* | |
* From WordPress 6.3 onwards, the `wp_register_script` function accepts an `$args` array that | |
* can include a `strategy` key with a value of either `async` or `defer`. | |
* | |
* This helper function handles the backwards compatibility for older versions of WordPress. When a | |
* `strategy` key is present in the `$args` array (and is either `defer` or `async`), the | |
* `script_loader_tag` filter is used to add the attribute to the script tag. | |
* | |
* Note that for older versions of WordPress, dependencies is not considered - the attribute is added unconditionally. | |
* | |
* When support for WP<6.3 is no longer required, simply replace all instances of this function with | |
* `wp_register_script()`. | |
* | |
* @see wp_register_script() | |
* | |
* @param string $handle Name of the script. Should be unique. | |
* @param string|false $src Full URL of the script, or path of the script relative to the WordPress root directory. | |
* If source is set to false, script is an alias of other scripts it depends on. | |
* @param string[] $deps Optional. An array of registered script handles this script depends on. Default empty array. | |
* @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL | |
* as a query string for cache busting purposes. If version is set to false, a version | |
* number is automatically added equal to current installed WordPress version. | |
* If set to null, no version is added. | |
* @param array|bool $args { | |
* Optional. An array of additional script loading strategies. Default empty array. | |
* Otherwise, it may be a boolean in which case it determines whether the script is printed in the footer. Default false. | |
* | |
* @type string $strategy Optional. If provided, may be either 'defer' or 'async'. | |
* @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'. | |
* } | |
* @return bool Whether the script has been registered. True on success, false on failure. | |
*/ | |
function wpnext_register_script( $handle, $src, $deps, $ver, $args ) { | |
// If >= 6.3, re-use wrapper function signature. | |
if ( version_compare( strtok( get_bloginfo( 'version' ), '-' ), '6.3', '>=' ) ) { | |
wp_register_script( | |
$handle, | |
$src, | |
$deps, | |
$ver, | |
$args | |
); | |
} else { | |
wp_register_script( | |
$handle, | |
$src, | |
$deps, | |
$ver, | |
isset( $args['in_footer'] ) ? $args['in_footer'] : false | |
); | |
if ( isset( $args['strategy'] ) ) { | |
wp_script_add_data( $handle, 'strategy', $args['strategy'] ); | |
} | |
} | |
} | |
/** | |
* Enqueue scripts with a `defer` or `async` strategy in a backwards compatible manner. | |
* | |
* From WordPress 6.3 onwards, the `wp_enqueue_script` function accepts an `$args` array that | |
* can include a `strategy` key with a value of either `async` or `defer`. | |
* | |
* This helper function handles the backwards compatibility for older versions of WordPress. When a | |
* `strategy` key is present in the `$args` array (and is either `defer` or `async`), the | |
* `script_loader_tag` filter is used to add the attribute to the script tag. Note that | |
* for older versions of WordPress, dependency is not managed and the attribute is added unconditionally. | |
* | |
* When support for WP<6.3 is no longer required, simply replace all instances of this function with | |
* `wp_enqueue_script()`. | |
* | |
* @see wp_enqueue_script() | |
* | |
* @param string $handle Name of the script. Should be unique. | |
* @param string $src Full URL of the script, or path of the script relative to the WordPress root directory. | |
* Default empty. | |
* @param string[] $deps Optional. An array of registered script handles this script depends on. Default empty array. | |
* @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL | |
* as a query string for cache busting purposes. If version is set to false, a version | |
* number is automatically added equal to current installed WordPress version. | |
* If set to null, no version is added. | |
* @param array|bool $args { | |
* Optional. An array of additional script loading strategies. Default empty array. | |
* Otherwise, it may be a boolean in which case it determines whether the script is printed in the footer. Default false. | |
* | |
* @type string $strategy Optional. If provided, may be either 'defer' or 'async'. | |
* @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'. | |
* } | |
*/ | |
function wpnext_enqueue_script( $handle, $src, $deps, $ver, $args ) { | |
wpnext_register_script( $handle, $src, $deps, $ver, $args ); | |
wp_enqueue_script( $handle ); | |
} | |
if ( version_compare( get_bloginfo( 'version' ), '6.3', '<' ) ) { | |
add_filter( | |
'script_loader_tag', | |
static function( $tag, $handle ) { | |
$strategy = wp_scripts()->get_data( $handle, 'strategy' ); | |
if ( in_array( $strategy, array( 'async', 'defer' ), true ) && false === strpos( $tag, $strategy) ) { | |
$tag = str_replace( '<script ', '<script ' . $strategy . ' ', $tag ); | |
} | |
return $tag; | |
}, | |
10, | |
2 | |
); | |
} |
@adamsilverstein Here's a hybrid of the two approaches: westonruter/9694840a1cb940e66bdfb650e34c325e
Great!
Also I think the wrapper for wp_enqueue_script() can reduce a lot of code by simply calling wp_enqueue_script() after calling the wrapper for wp_register_script().
Nice!
I'll update here based on that (except the WP_HTML_Tag_Processor
bit so the code can work with older WP versions)
@adamsilverstein I would just suggest making the replacement logic a bit more robust, for example:
$tag = str_replace( '<script ', '<script ' . $args['strategy'] . ' ', $tag );
This would prevent situations where the occurrence of ' src'
somewhere in the string is mistakenly replaced, for example in this string:
<script src="/foo.js" class="foo-js src-local"></script>
@adamsilverstein I would just suggest making the replacement logic a bit more robust, for example:
Good suggestion. Done!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@adamsilverstein Here's a hybrid of the two approaches: https://gist.github.com/westonruter/9694840a1cb940e66bdfb650e34c325e
Also I think the wrapper for
wp_enqueue_script()
can reduce a lot of code by simply callingwp_enqueue_script()
after calling the wrapper forwp_register_script()
.