0

I've written a module which loads CSS and JS files using the #attached property. This works fine across most themes, but for some themes the files are not loading.

e.g.:

$page['header']['#attached']['css'][] = array(
  'data' => drupal_get_path('module', 'mymodule') . '/css/mymodule_default.css',
  'type' => 'file',
  'options' => array(
    'group' => CSS_THEME,
    'preprocess' => TRUE,
    'every_page' => TRUE, 
  ),
);

As there is very little by way of explanation on #attached (most articles repeat the same stuff) I can't figure out where the problem may lie. Using drupal_add_css() and drupal_add_js() works, but is not recommended for caching reasons, and I prefer to pass settings using attached for this reason.

The #attached properties are showing in the $page array but are not being rendered.

In summary, are there situations when certain themes block the correct rendering of #attached elements that would normally be applied to a render structure by drupal_process_attached()

Any help is appreciated.

2
  • Possible idea? If there isn't a header region in the theme, then drupal_render() would never be called to process this.
    – Jimajamma
    Commented Jan 18, 2014 at 17:06
  • Thanks, I've already checked that. Anyway the problem remains if I change the region to suit the theme.
    – Pluto
    Commented Jan 18, 2014 at 17:14

1 Answer 1

3

The example code you are giving indicates, that you want to have this CSS included on every page. In this case do not bother using #attached at all and just call drupal_add_css from hook_page_build. An even better option is to include the CSS in the module-info file with the following line:

stylesheets[all][] = css/mymodule_default.css

In contrast if the asset is to be added to the page only when a given element is rendered (e.g. a form), then use #attached on that element. This way the path to the asset gets stored along with the render-array if the element is cacheable. On subsequent page requests, the element will be retrieved from the cache and the asset is attached to the page. If you would use drupal_add_css together with cacheable elements, the asset would be missing on subsequent page builds.

Note that even when using #attached, drupal_add_css is called internally from within drupal_process_attached. The responsible code, but actually boils down to this excerpt:

// Add both the JavaScript and the CSS.
// The parameters for drupal_add_js() and drupal_add_css() require special
// handling.
foreach (array('js', 'css') as $type) {
  foreach ($elements['#attached'][$type] as $data => $options) {
    // [...]
    call_user_func('drupal_add_' . $type, $data, $options);
  }
  unset($elements['#attached'][$type]);
}

The call_user_func will invoke either drupal_add_js and drupal_add_css respectively, depending the value of $type. It follows that there is no additional benefit from using #attached unless the asset is used by a render-array which is to be stored in the cache. Assets specified via #attached are guaranteed to be loaded if the associated render-array is rendered. They will not be added to the page when the associated render-array is not rendered.

In your specific situation you are attaching the CSS not to an element but to a page region. However the header-region apparently is not rendered by every theme, probably because it is not widely used. The header-region is rendered in page.tpl.php. If a theme chooses to ignore a region, like the Versatile and the Corporate Clean do, the CSS will not be attached.

To sum up:

  • Use #attached when adding assets to cacheable render-arrays (especially forms and form elements)
  • Add stylesheets / scripts to the module-info file or call drupal_add_js and drupal_add_css whenever adding assets globally.
14
  • Great explanation, it's arguably missing one small bit - to include CSS/js on every page it's at least equally appropriate to add via a module/theme .info file
    – Clive
    Commented Jan 18, 2014 at 14:04
  • hook_init should not be used to load CSS/JS files using drupal_add_.. My question relates to adding files to the $page array for example using hook_page_alter(), hence the $page variable, as this is a recommended way to attach CSS/JS files and bypass the caching that occurs with drupal_add_js()/css. Also using .info is valid but only if you don't want to use conditional logic to load files, such as as a result of a variable state.
    – Pluto
    Commented Jan 18, 2014 at 14:46
  • Thanks for the response above, but the question isn't asking how #attached works vs drupal_add_js/css, but whether there are situations when certain themes block the correct rendering of #attached elements that would normally be applied to a render structure by drupal_process_attached()
    – Pluto
    Commented Jan 18, 2014 at 15:02
  • 1
    @Pluto: Please name an example theme for which your code works and another where it does not.
    – znerol
    Commented Jan 18, 2014 at 16:36
  • 1
    Updated the answer and switched from hook_init to hook_page_build. Actually it does not really change the situation here but it is better practice to only add CSS to HTML pages, and let alone Ajax callbacks etc.
    – znerol
    Commented Jan 18, 2014 at 19:00

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.