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().

$build['#attached']['drupal_add_http_header'] = array(
  array('Content-Type', 'application/rss+xml; charset=utf-8'),
);

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:

$og_image = array(
  '#tag' => 'meta', 
  '#attributes' => array(
    'property' => 'og:image',
    'content' => $image_path,
  ),
);
$build['#attached']['drupal_add_html_head'] = array(
  array($og_image, 'og_image'),
);

The possibilities are endless!

Some other interesting uses in Core

Filed under:

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?

Wow, nice find, I can already think of a few places where this would be useful...

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)

This post gave me some nice ideas. I'll play a bit with this.
Thanks for sharing.

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.

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <cpp>, <java>, <php>. The supported tag styles are: <foo>, [foo].
  • Web page addresses and email addresses turn into links automatically.
  • Lines and paragraphs break automatically.

Ready for transformation?