11

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?

4 Answers 4

10

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.

3
  • Could you please specify what $l10n is? I understand passing it the handle and object, but not $l10n. Thanks.
    – Eric Leroy
    Commented Dec 22, 2013 at 10:14
  • 1
    @EricLeroy It is the third parameter of wp_localize_script(): a single or multi-dimensional array.
    – fuxia
    Commented Dec 22, 2013 at 13:54
  • This implementation helpen me very much, but I want to warn you that it messed up my admin scripts big time, ACF stopped working because the javascripts weren't printed due to above solution. I'm haven't got a fix yet, but I'm searching for it now. Commented Oct 31, 2017 at 11:17
5

@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;
}
2

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.

6
  • 1
    I had another way of doing this, which was to add save the two acf script tags $acf_field_group = $GLOBALS['wp_scripts']->registered['acf-field-group']; (also acf-input) and then add them again to the instance of the extended WP_Scripts tag: $GLOBALS['wp_scripts']->registered['acf-field-group'] = $acf_field_group, then realized that ACF is only using the scripts within Admin and I'm only l10n in front so just wrapped the action and filter in a !is_admin test.
    – MikeiLL
    Commented Jan 22, 2018 at 2:08
  • Good point, performance-wise your solution works better. I'm also adding the is_admin check to my personal version of this code. The thing I still like though about my approach is that if the id's of the scripts were to change in the future or there are new scripts missing in later versions of the ACF plugin (or other plugins), I don't have to change my code. Commented Jan 22, 2018 at 12:25
  • Yes that makes sense to me as well. I'm not sure if any of it is necessary if you're only running the action on the front end.
    – MikeiLL
    Commented Jan 22, 2018 at 22:50
  • Hmm.. touché. I guess sometimes I get caught up so much in finding the perfect solution that I forget what the initial problem was really about. However, if the need for it in the admin might ever change we already did the math ;-) Commented Jan 22, 2018 at 23:14
  • Same here, friend, but it's all about learning, ain't it?
    – MikeiLL
    Commented Jan 25, 2018 at 18:45
0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.