How to intercept already localized scripts

If a plugin uses some script (prominent example: jQuery UI Datepicker), but you’re not happy with how the script renders the output, then there’re two possibilities:

1. Unregister the script > Add your own version

So first you’d need to check the handle, then find the priority and the hook (wp_enqueue_scripts, login_enqueue_scripts, etc.) … you know the drill.

2. Change the jQuery plugin parameters

Normally – if the plugin isn’t crap – it pushes through the parameters from PHP to JS using

wp_localize_script( $handle, $object_name, array( 
    // data
) );

Now this is a smart way of adding your data to a JS script, but … it’s not filterable by default. Neither WP_Scripts nor WP_Dependencies offers any filter users can later utilize

Question: How can we filter the arguments/parameters that are moved from PHP to Javascript using wp_localize_script?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

wp_localize_script() calls the method localize() on the global variable $wp_scripts. We can set this variable to an instance of a child class of WP_Scripts:

class Filterable_Scripts extends WP_Scripts
{
    function localize( $handle, $object_name, $l10n )
    {
        $l10n = apply_filters( 'script_l10n', $l10n, $handle, $object_name );
        return parent::localize($handle, $object_name, $l10n);
    }
}

add_action( 'wp_loaded', function() {
    $GLOBALS['wp_scripts'] = new Filterable_Scripts;
});

The theme customizer doesn’t use that, it creates a separate instance of WP_Scripts (see wp-admin/customize.php). It might be possible to replace that too:

add_action( 'customize_controls_init', function() {
    $GLOBALS['wp_scripts'] = new Filterable_Scripts;
    $GLOBALS['wp_scripts']->registered = $GLOBALS['registered'];
});

None of this has been tested, just an idea.

Method 2

@toscho great implementation. Tested and true. Here is a slightly modified version, which also passes the $handle and $object_name so you can filter only when needed.

class Filterable_Scripts extends WP_Scripts
{
    function localize( $handle, $object_name, $l10n )
    {
        $l10n = apply_filters( 'script_l10n', $l10n, $handle, $object_name );
        return parent::localize($handle, $object_name, $l10n);
    }
}

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

add_filter('script_l10n', 'se108362_example_filter', 10 , 3);

// Example
function se108362_example_filter($l10n, $handle, $object_name ) {
    if('js-handle' == $handle && 'jsVariable' == $object_name) {
       return 'Something Else';
    }
    return $l10n;
}

Method 3

The accepted answer is great! But I ran into a problem that Advanced Custom Fields stopped working in the backend due to a javascript error. After digging for a few hours I came to the conclusion that the Filterable_Scripts object was missing the javascript files registered by the ACF plugin. I don’t know exactly why it did this, but I’ve found a proper solution to this if you run into the same problem.

The $GLOBALS['wp_scripts'] fortunately still contained the proper scripts. So i did the following in the add_action:

add_action( 'wp_loaded', function() {
    $fscripts = new Filterable_Scripts();

    $missing_scripts = array_diff_key( $GLOBALS['wp_scripts']->registered, $fscripts->registered);
    foreach($missing_scripts as $mscript){
        $fscripts->registered[$mscript->handle] = $mscript;
    }

    $GLOBALS['wp_scripts'] = $fscripts;
});

Because the object contains an array of all registered scripts and the handles are also the array keys, I could use array_diff_key to determine which scripts were missing from the extended object and re-add them. I did this and not just

$fscripts->registered = $GLOBALS['wp_scripts']->registered;

because I didn’t want to overwrite any changes made by the extended object.

Method 4

Building on the work of everyone in this thread, I extended the answers to filter the <script> tag itself, not solely it’s inner contents. This is needed, for instance, to mark the inline scripts with data-cfasync="false" when using Cloudflare Rocket Loader.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x