Shomeya

Two web artists. One amazing company.

Getting used to #attached

from Michael Prasuhn on January 6, 2011 03:00pm

In the course of building our new website on Drupal 7 I wanted to get to know some of the cooler new features that are now available, after spending the last three years salivating over commits and code that wasn't always 100% stables (even though it was 99.99% stable for the most part).

One of these new features is the #attached property which allows you to attach Javascript or CSS to any point in a render array structure and let the render API make sure that it ends up in the appropriate place in the header, footer, inline or external, whatever. You can read about the changes compared to Drupal 6 code on the Converting 6.x modules to 7.x page on Drupal.org. In short, you don't need to call drupal_add_css() or drupal_add_js() anymore, although that method still works fine. The advantages of using the render API quickly become apparent should you want to modify the JS or CSS attached to an element from another section of code. Although the online API docs don't indicate this, it works just fine with render arrays, not just forms.

An example

We wanted to add simple effect from the jQuery Inline Fields plugin to the search box at the top of our site. This required including an external Javascript file and a small portion of inline javascript to trigger the effect on the field. Here's the code that it takes to make this happen (I'll explain this in more detail below):

<?php
/**
* Implements hook_form_FORM_ID_alter().
*/
function shomeya_theme_form_search_block_form_alter(&$form, &$form_state, $form_id) {
  $form['search_block_form']['#title_display'] = 'before';
  $form['#attached']['js'] = array(
    array(
      'data' => drupal_get_path('theme', 'shomeya_theme') . '/js/infields.js',
      'options' => array(
        'group' => JS_LIBRARY,
        'preprocess' => TRUE,
        'every_page' => TRUE,
      ),
    ),
    "jQuery('#search-block-form label').inFieldLabels({fadeOpacity: 0.5, fadeDuration: 150});" => array('type' => 'inline', 'group' => JS_THEME, 'scope' => 'footer'),
  );
}
?>

First off, you may have noticed that this was placed in our theme. Yup, it turns out that in Drupal 7, themes can implement several common alter hooks This is great since this isn't really a change that affect any functionality, it's pure presentation. This lets us keep UI focused Javascript and code in our theme, and the themers and developers can stay out of each others way very easily, even if the code would be identical if it was in a module.

Secondly, since we are modifying the search block form the first thing we had to do to make the Inline Fields plugin work was make sure that the label was visible. In past versions of Drupal, or in many contributed themes this would be done with targeted CSS, and while the form label is still hidden with CSS, it's not a case of targeted CSS anymore. The form element was wrapped in a class of 'element-invisible' which is a new feature in Drupal 7 that helps many individuals using your site with screen reader software. This is an awesome feature but we needed to disable it. By examining the non-rendered structure of the form, we can see that there is a new '#title_display' element responsible for this class, simply changing that to before, removed the 'element-invisible' class and we were on our way.

Using #attached

As you can see from the code sample above, #attached can take data in multiple ways. If the key of the element inside of #attached is numeric, it looks for a 'data' element, and reads in extra items from and 'options' array. This is how the Inline Fields JS file is included. On line 16 of the snippet you'll see the other method of using #attached, which is where the element's key is a string, and the element itself is an array of options. Here you can see that we have a 1 line snippet to target the #search-block-form label, and call the inFieldLabels method on it. We've declared this an inline script, set to be output in the footer, and grouped with other items coming from the theme. All of these options and more are documented with drupal_add_js on api.drupal.org.

While this doesn't seem like a huge change, it subtlety affects the way that processing and alterations are performed in Drupal 7, and hopefully will catch on as a primary method for those doing tiny alters to other forms, or structures to add their JS and CSS to forms, in a way that others can easily manipulate. I'd love to hear and see what other uses for #attached that developers are coming up with. If you have an interesting example, please share it in the comments and let us know how Drupal 7 is changing the way you develop.

blog comments powered by Disqus