How to add extra attributes to the script tag added via wp_localize_script()

I also found that Cloudflare Rocket Loader would cause javascript errors in this situation. When the .js file was marked to be ignored, but the inline js was unable to be so marked, undefined errors would occur when the script tried to access the async-loaded inline data.

The approach detailed in How to intercept already localized scripts only allows you to change the data in the localized script tags. I.e:

var wc_some_params = {...}

It does not give you access to the outer <script></script> tags. To add the data-cfasync="false" attribute to the script tag I extended WP_Script and overloaded the print_extra_script() function in order to filter the full tag.

1. Create child class of WP_Script with the new filter

/**
* Extends WP_Scripts class to filter inline script tags added via wp_localize_script().
*/
class WP_Filterable_Scripts extends WP_Scripts {

    private $type_attr;

    /**
    * Executes the parent class constructor and initialization, then copies in the 
    * pre-existing $wp_scripts contents
    */
    public function __construct() {
        parent::__construct();

        if (
            function_exists( 'is_admin' ) && ! is_admin()
            &&
            function_exists( 'current_theme_supports' ) && ! current_theme_supports( 'html5', 'script' )
        ) {
            $this->type_attr = " type="text/javascript"";
        }

        /**
        * Copy the contents of existing $wp_scripts into the new one.
        * This is needed for numerous plug-ins that do not play nice.
        *
        * https://wordpress.stackexchange.com/a/284495/198117
        */
        if ( $GLOBALS['wp_scripts'] instanceof WP_Scripts ) {
            $missing_scripts = array_diff_key( $GLOBALS['wp_scripts']->registered, $this->registered );
            foreach ( $missing_scripts as $mscript ) {
                $this->registered[ $mscript->handle ] = $mscript;
            }
        }
    }

    /**
     * Adapted from wp-includes/class.wp-scripts.php and added the
     * filter `wp_filterable_script_extra_tag`
     *
     * @param string $handle
     * @param bool $echo
     *
     * @return bool|mixed|string|void
     */
    public function print_extra_script( $handle, $echo = true ) {
        $output = $this->get_data( $handle, 'data' );
        if ( ! $output ) {
            return;
        }

        if ( ! $echo ) {
            return $output;
        }

        $tag = sprintf( "<script%s id='%s-js-extra'>\n", $this->type_attr, esc_attr( $handle ) );

        /**
        * Filters the entire inline script tag.
        *
        * @param string $tag    <script type="text/javascript" id="plug-js-extra">...</script>
        * @param string $handle Script handle.
        */
        $tag = apply_filters( 'wp_filterable_script_extra_tag', $tag, $handle );

        // CDATA is not needed for HTML 5.
        if ( $this->type_attr ) {
            $tag .= "/* <![CDATA[ */\n";
        }

        $tag .= "$output\n";

        if ( $this->type_attr ) {
            $tag .= "/* ]]> */\n";
        }

        $tag .= "</script>\n";

        echo $tag;

        return true;
    }
}

2. Create new, filterable $wp_scripts object

The new WP_Filterable_Scripts class can be instantiated:

add_action( 'init', function () {
    $fscripts              = new WP_Filterable_Scripts;
    $GLOBALS['wp_scripts'] = $fscripts;
}, 100 );

3. Filter the tags to add data-cfasync attribute when needed

I wanted to keep a blacklist of handles to tag, so here’s the use of an array of regexes. Any handle that matches will get the data-cfasync="false" tag inserted on the enqueued js file as well as the inline script.

function add_cfasync( $tag, $handle ) {
    $attr    = "data-cfasync="false" ";
    $filters = array(
        '/^hoverintent.*$/',
        '/^admin-bar$/',
        '/^jquery.*$/',
        '/.*event.*$/',
        '/^query-monitor$/',
        '/^wpmenucart-ajax-assist$/',
    );

    if ( ! is_admin() ) {
        foreach ( $filters as $exclude_regex ) {
            if ( preg_match( $exclude_regex, $handle ) != false ) {
                $tag = str_replace( '<script ', '<script ' . $attr, $tag );
            }
        }
    }

    return $tag;
}
add_filter( 'wp_filterable_script_extra_tag', 'add_cfasync', 0, 2 );
add_filter( 'script_loader_tag', 'add_cfasync', 0, 2 );

If you only wish to modify the wp_localize_script() inlines, remove the second add_filter line.

Blacklisting the necessary handles has resolved all my Rocket Loader errors in WordPress.

Edit

I have subsequently found that filtering solely the wp_localize_script() inline scripts is sufficient to eliminate all Rocket Loader javascript errors on my WordPress site.

add_filter( 'wp_filterable_script_extra_tag', 'add_cfasync', 0, 2 );

In my case Rocket Loader can handle all external js without error, as long as certain localized scripts have cfasync="false". YMMV.

Leave a Comment