
What I Learned Today: Drupal #attached Awesomeness
What is #attached
Drupal 7 introduced render arrays, which was really an expansion of the existing Form API. One of the special elements of a render array is the #attached key. In a nutshell, it allows you to attach additional CSS or Javascript to your element. Here is a typical example that I stole straight from the Color module.
// Add scheme selector. $form['scheme'] = array( '#type' => 'select', '#title' => t('Color set'), '#options' => $color_sets, '#default_value' => $scheme_name, '#attached' => array( // Add Farbtastic color picker. 'library' => array( array('system', 'farbtastic'), ), // Add custom CSS. 'css' => array( $base . '/color.css' => array(), ), // Add custom JavaScript. 'js' => array( $base . '/color.js', array( 'data' => array( 'color' => array( 'reference' => color_get_palette($theme, TRUE), 'schemes' => $schemes, ), 'gradients' => $info['gradients'], ), 'type' => 'setting', ), ), ), );
There are three different child keys of #attached in use here: library
, css
and js
. Each is an array, and the values of each will later be passed to the corresponding drupal_add_library, drupal_add_css, drupal_add_js. This is nice because then we can load only the css/javascript that is needed on that page and it is in-context with where it is needed instead of having some complex logic in a hook_init() to conditionally load it.
As If That Isn't Awesome Enough
What I didn't know until recently, is that library, css and js are not the only keys you can pass. You can actually pass any arbitrary key you want, and it will be used as a callback function, receiving as arguments the values of its array. Here is an example from the documentation for drupal_process_attached().
So when drupal_process_attached runs, it will call: drupal_add_http_header('Content-Type', 'application/rss+xml; charset=utf-8');
Another example might be:
The possibilities are endless!
Some other interesting uses in Core
- Field UI Module uses drupal_add_tabledrag
- drupal_add_html_head_link
- _drupal_default_html_head
Want more awesome content like this? Check out our top 20 Drupal tips of all time!
Comments
Interesting. Shouldn't 'drupal_add_http_header' actually be 'http_header'? Otherwise Drupal will try to call drupal_add_drupal_add_httml_header(), no?
Mon, 12/16/2013 - 22:01
'js', 'css', and 'library' get special handling. Other types are executed directly without altering the callback name. See:
https://api.drupal.org/api/drupal/includes%21common.inc/function/drupal…
Mon, 12/16/2013 - 23:31
Wow, nice find, I can already think of a few places where this would be useful...
Tue, 12/17/2013 - 00:53
FYI, render arrays were introduced in Drupal 5 for the rendering of nodes. Their use was expanded in Drupal 7 to the rendering of the whole page (i.e. blocks/regions).
It is worth noting the reason why we use #attached over just calling drupal_add_css() and friends is that #attached plays nicely with render caching (which *was* introduced in D7)
Tue, 12/17/2013 - 02:47
This post gave me some nice ideas. I'll play a bit with this.
Thanks for sharing.
Tue, 12/17/2013 - 18:58
FYI: using #attached
is mandatory in Drupal 8. All the drupal_add_*()
functions are being retired (because they're incompatible with render caching). So start using it today, and the migration to Drupal 8 will be a little bit easier :)
If you want to understand better why it's incompatible with render caching, see http://wimleers.com/talk/really-fast-drupal-8.
Mon, 12/16/2013 - 21:10