32

Within the WP_Dependencies class exists a method named add_data. This function adds data to scripts/styles that have been enqueued during the WordPress load. A commonly cited use for this function is to add a conditional when adding stylesheets that are targeted at different versions of IE. For example, to target IE8 and lower:

function test_wp_print_styles() {
    global $wp_styles;

    wp_enqueue_style( 'test-style', get_template_directory_uri() . '/css/test.css', array(), 1, 'all' );
    $wp_styles->add_data( 'test-style', 'conditional', 'lte ie8' );
}
add_action( 'wp_print_styles', 'test_wp_print_styles' );

This will render as:

<!--[if lte ie8]>
<link rel='stylesheet' id='test-style-css'  href='http://trunkosaurus.dev/wp-content/themes/twentyeleven/css/test.css?ver=1' type='text/css' media='all' />
<![endif]--> 

When I look through Core, I see a handful of places where this method is used:

  • WP_Styles->add_inline_style() : adds inline style after the referenced stylesheet (done via WP_Styles->print_inline_style())

  • WP_Scripts->localize() : adds a json encoded object (wrapped by the more "public" wp_localize_script() function)

  • wp_plupload_default_settings() : adds json encoded object (created from a multidimensional array) for the 'wp-plupload' script (note that this is forthcoming in 3.4)

  • When registering/enqueueing scripts and styles Adding data for default scripts (wp-includes/script-loader.php)

From reading through the uses of the method, it does not appear to have a specific use case. In wp_plupload_default_settings, it seems to allow for arbitrary data injection. In wp_register_script, it seems to be used to differentiate between header and footer scripts. In add_inline_style, it is used to denote inline style that should be added after a specified stylesheet is enqueued.

An excellent use for this function would be something like the following code where you are enqueuing an external script but need to send it some config vars, some of which come from the DB:

function zdt_enqueue_add_this() {
    global $wp_scripts;

    wp_enqueue_script( 'zdt-add-this', 'http://s7.addthis.com/js/250/addthis_widget.js#pubid=myidhere' );

    // Contrived example of database call to get a twitter handle stored in the db
    $author_twitter_handle = zdt_get_twitter_handle();

    $js = "var addthis_share = { templates : { twitter: '{{title}} {{url}} (by @" . sanitize_key( $author_twitter_handle ) . "' } };\n";
    $js .= 'var addthis_config = { ui_header_color: "#FFFFFF", ui_header_background: "#FA9628", ui_cobrand: "My Site" };';

    $wp_scripts->add_data( 'zdt-add-this', 'data', $js );
}
add_action( 'wp_enqueue_scripts', 'zdt_enqueue_add_this' );

This will result in:

<script type='text/javascript'>
/* <![CDATA[ */
var addthis_share = { templates : { twitter: '{{title}} {{url}} (by @tollmanz' } };
var addthis_config = { ui_header_color: "#FFFFFF", ui_header_background: "#FA9628", ui_cobrand: "My Site" };
/* ]]> */
</script>
<script type='text/javascript' src='http://s7.addthis.com/js/250/addthis_widget.js?ver=3.4-beta4-20731#pubid=myidhere'></script>

Note that this cannot be accomplished with wp_localize_script because the addthis_share object has properties within properties (I did write about a somewhat hacky way around this before).

EDIT: I was wrong in stating this. wp_localize_script handles multidimensional arrays just fine.

This method seems to work really well for the following reasons:

  1. It allows you to attach the data to the script handle so it is always properly enqueued with the script. Further, it will be intelligent about deenqueueing the script, script order, and script placement.
  2. It allows you to use PHP to send vars to JS.
  3. It seems more organized than using wp_print_styles to print out some arbitrary script that is acted on later by an enqueued script.

There are some things that do not work as expected that worries me about this method. One such issue is that if you use wp_localize_script along with $wp_scripts->add_data, you can get unexpected results. For instance:

// Contrived example of database call to get a twitter handle stored in the db
$author_twitter_handle = zdt_get_twitter_handle();

$js = "var addthis_share = { templates : { twitter: '{{title}} {{url}} (by @" . sanitize_key( $author_twitter_handle ) . "' } };\n";
$js .= 'var addthis_config = { ui_header_color: "#FFFFFF", ui_header_background: "#FA9628", ui_cobrand: "My Site" };';

$wp_scripts->add_data( 'zdt-add-this', 'data', $js );
wp_localize_script( 'zdt-add-this', 'addthis_share', array( 'var' => 'val' ) );

Produces:

<script type='text/javascript'>
/* <![CDATA[ */
var addthis_share = { templates : { twitter: '{{title}} {{url}} (by @tollmanz' } };
var addthis_config = { ui_header_color: "#FFFFFF", ui_header_background: "#FA9628", ui_cobrand: "My Site" };
var addthis_share = {"var":"val"};
/* ]]> */
</script>
<script type='text/javascript' src='http://s7.addthis.com/js/250/addthis_widget.js?ver=3.4-beta4-20731#pubid=myidhere'></script>

Whereas this script:

// Contrived example of database call to get a twitter handle stored in the db
$author_twitter_handle = zdt_get_twitter_handle();

$js = "var addthis_share = { templates : { twitter: '{{title}} {{url}} (by @" . sanitize_key( $author_twitter_handle ) . "' } };\n";
$js .= 'var addthis_config = { ui_header_color: "#FFFFFF", ui_header_background: "#FA9628", ui_cobrand: "My Site" };';

wp_localize_script( 'zdt-add-this', 'addthis_share', array( 'var' => 'val' ) );
$wp_scripts->add_data( 'zdt-add-this', 'data', $js );

Produces:

<script type='text/javascript'>
/* <![CDATA[ */
var addthis_share = { templates : { twitter: '{{title}} {{url}} (by @tollmanz' } };
var addthis_config = { ui_header_color: "#FFFFFF", ui_header_background: "#FA9628", ui_cobrand: "My Site" };
/* ]]> */
</script>
<script type='text/javascript' src='http://s7.addthis.com/js/250/addthis_widget.js?ver=3.4-beta4-20731#pubid=myidhere'></script>

The data key that is set by wp_localize_script is ultimately overwritten by the call to $wp_scripts->add_data, whereas if you call wp_localize_script twice for the same script, the string will be properly concatenated.

While all of this is a really handy way of printing arbitrary script for use with an enqueued script, it makes me think that it should not be widely used because of potential of conflicts. I certainly can see an argument for using this in personal projects where the code will not be used in community plugins/themes.

I also looked at Core Trac to see if there was any clues as to the purpose of the function. I found one ticket (http://core.trac.wordpress.org/ticket/11520) (an epic one at that) that explored other ways of adding arbitrary JS. So it seems that there is interest in creating a better way to add arbitrary JS, but not sure exactly if add_data should be part of the process.

My main question is: should developers be using this function? In some cases (e.g., wp_register_script), it seems like a "private" function that 3rd parties should not use; however, in other cases (e.g., wp_plupload_default_settings), it seems like a perfectly reasonable way to inject arbitrary JS prior to an enqueued script.

I don't imagine there is a "correct" answer to this, but would love to hear what other devs think. I also imagine that there are pieces to this puzzle that I've completely neglected and would love to hear what others have to say about it.

2 Answers 2

4

This function adds data to scripts/styles that have been enqueued during the WordPress load.

Not really. It adds data to scripts/styles that've been registered.

The data key that is set by wp_localize_script is ultimately overwritten by the call to $wp_scripts->add_data, whereas if you call wp_localize_script twice for the same script, the string will be properly concatenated.

Right. They both call the underlying (non-accessible, internal) API, so it gets overwritten (as you stated). This happens when it calls $this->get_data( $handle, 'data' );.

Question

My main question is: should developers be using this function?

Answer

Simply said: Yes, when you got no other chance to do what you need.

Another example: Check if a script was registered (for e.g. json2/jquery) and move it to the footer (check extra['group']).

// Move scripts to the footer - in case it isn't already there
if ( ! $wp_scripts->get_data( 'json2', 'group' ) )
    $wp_scripts->add_data( 'json2', 'group', 1 );

if ( ! $wp_scripts->get_data( 'jquery', 'group' ) )
    $wp_scripts->add_data( 'jquery', 'group', 1 );

Note: This ↑ only works for data filed under extra!

Additional notes

Counter-Question: Have you ever tried to add dependencies to scripts registered by core? For e.g.: Try to add JSON2 as needed deps to jQuery. This isn't possible without intercepting the global $wp_scripts:

global $wp_scripts;

$scripts = array( 
     'jquery'      => array( 'json2' )
    ,'jquery-form' => array( 'json2' ) 
);

foreach ( $scripts as $handle => $deps )
{
    // Ugly hack: Intercept the global to force the "natural"/needed order: JSON2 » jQuery
    $deps_default =& $wp_scripts->registered[ $handle ]->deps;
    $wp_scripts->registered[ $handle ]->deps = array_merge( $deps_default, $deps );
}

There's a whole load of things the class can't do. So using something like ->add_data() is imo fully valid. Just use what you got, as it's still better then living the lacks of core classes.

5
  • "Wait until core adds a possiblity to add dependencies to default & built-in scripts " Did you open a ticket in trac?
    – scribu
    Commented May 17, 2012 at 14:42
  • @scribu Thanks, but no, I haven't and no, I won't. All of my tickets simply rot there, so I stepped back from investing effort into trac tickets. That's no offense, simply a conclusion from what I've experienced so far. But to not start arguing with you, I'll remove it as this is just left from a simple copy/paste from one of my plugins.
    – kaiser
    Commented May 17, 2012 at 15:35
  • Well, then I guess I'll have to ask here: what would be the benefit of loading JSON2 before jQuery?
    – scribu
    Commented May 17, 2012 at 21:18
  • Nothing, as it's an abstract example. If you'd try to find a more detailed example, then you could imagine one library that needs JSON2, but also needs to get loaded before jQuery: Let's name it UberjQuery. Btw: As you're doing pretty well with diggin' into core, why don't you take the time and write an answer? I guess it would be worth the time reading.
    – kaiser
    Commented May 17, 2012 at 22:47
  • Thanks for your thoughts Kaiser! I am definitely looking for methods to add JS that is supported by the "API". While I know I can bend it to do all sorts of things, that can lead to unstable code. It's nice to know what it is intended for rather than what it can do, and, certainly, a lot can be done with it.
    – tollmanz
    Commented May 18, 2012 at 2:58
1

There was a big debate in WP 3.3 about how to handle script data:

http://core.trac.wordpress.org/ticket/11520

Note that you can pass nested arrays to wp_localize_data() now:

wp_localize_script( 'jquery', 'jQueryL10n', array(
    'foo' => array(
        'bar' => array( 'apple', 'orange' )
    ),
) );

So, I would use add_data() if there was no higher-level API for what I needed to do, with the understanding that it's behavior might change in some edge cases, such as when concatenation is involved.

7
  • Thank you for your input Scribu! Funny that you link to that ticket! I linked to it in my post, but there was so much going on in that that I did not catch that multidimensional arrays are now supported.
    – tollmanz
    Commented May 18, 2012 at 2:53
  • Ha...nice edit! I wasn't as confused by that ticket as I thought.
    – tollmanz
    Commented May 18, 2012 at 2:55
  • @tollmanz Yeah, it's pretty confusing, especially if you weren't in IRC at the time.
    – scribu
    Commented May 18, 2012 at 2:56
  • Mr. @ungestaltbar showed me a way to add multidimensional arrays some month ago. Didn't know, that this is already in core.
    – kaiser
    Commented May 18, 2012 at 9:03
  • @scribu - isn't there already support for multi-dimensional arrays? - at least I use them without any problems... Commented May 18, 2012 at 11:44

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.