How to add CSS classes to nodes, based on flags set

Last updated on
30 April 2025

A sort of "swiss army knife" when theming a node is to have a CSS class added to it for each flag set. So, for example, if the node is bookmarked, it will also have a "node-flagged-bookmarks" class. And if it's flagged as funny, it will also have a "node-flagged-funny" class.

2010-09-29: The Flag Classes module will be released soon. You won't need to use the recipes in this page anymore.

The code

To make this happen, put the following preprocess function in your 'template.php'. If you already have a function by the same name, change 'phptemplate' to the name of your theme (or replace it with the name of any module you have enabled). Don't forget to clear Drupal's cache or else this new code won't be picked up.

An added bonus: if a flag is set by the current user, a "node-flagged-FLAGNAME-self" class will be generated as well. For example, "node-flagged-funny" will be generated if anyone found the joke (the node) to be funny. But "node-flagged-funny-self" will be generated only if you found it as such.

/**
 * Generates a node-flagged-FLAGNAME css class for every flag set on the node.
 *
 * In addition, if the flag is set by the current user, a
 * node-flagged-FLAGNAME-self class will be generated as well.
 *
 * The class names are accumulated in the $flag_classes variable and you must 
 * use it in your 'node.tpl.php', or else it will have no effect.
 */
function phptemplate_preprocess_node(&$vars) {
  $vars['flag_classes'] = implode(' ', _phptemplate_get_flag_classes('node', $vars['node']->nid));
}

/**
 * Returns a list of CSS classes for a flagged item.
 *
 * The classes are of the form "node-flagged-FLAGNAME" (where "node" would be
 * "user" or "comment": depending on the item type).
 *
 * Additionally, if the flag is set by the current user, a
 * "node-flagged-FLAGNAME-self" class will be generated as well.
 *
 * @param $content_type
 *   The item type: Either "node" or "user" or "comment" or whatever.
 * @param $content_id
 *   The item ID.
 */
function _phptemplate_get_flag_classes($content_type, $content_id) {
  static $flags = array();

  if (!module_exists('flag')) {
    return array();
  }

  if (!isset($flags[$content_type])) {
    $flags[$content_type] = flag_get_flags($content_type);
  }

  // Note: is_flagged() and get_count() use internal cache,
  // so using them won't result in issuing excessive SQL queries.

  $classes = array();
  foreach ($flags[$content_type] as $flag) {
    $css_name = str_replace('_', '-', $flag->name);
    if (!$flag->global && $flag->is_flagged($content_id)) {
      // The item is flagged by me.
      $classes[] = $content_type . '-flagged-' . $css_name;
      $classes[] = $content_type . '-flagged-' . $css_name . '-self';
    } elseif ($flag->get_count($content_id)) {
      // The item is flagged by anybody.
      $classes[] = $content_type . '-flagged-' . $css_name;
    }
  }
  return $classes;
}

The class names are accumulated in the $flag_classes varibale. You should embed it in your 'node.tpl.php'. For example:

<div id="node-..." class="<?php echo $flag_classes; ?> ...

(For Drupal 5 instructions see older revisions of this page.)

Testing the code

To test the code add the following to your stylesheet:

/* Change "bookmarks" to the machine-name of the flag you're intereseted in. */

/* If this is a global flag, or you're interested in other people's flagging,
   remove the "-self" */

.node-flagged-bookmarks-self {
  background-color: yellow !important;
}

Flagged nodes should be painted in yellow.

Dynamically updating the CSS

Our code works, but it has one problem: when we flag items using the JavaScript link, the CSS classes aren't updated. This isn't surprising because the page isn't refreshed. Fortunately, it's easy to dynamically update the CSS classes using JavaScript. Use the following code:

(function ($) {

// Update the CSS classes of flagged items after their flag link is clicked.
$(document).bind('flagGlobalAfterLinkUpdate', function(event, data) {
  var class_names = data.contentType + '-flagged-' + data.flagName.replace(/_/g, '-');
  class_names += ' ' + class_names + '-self';
  var method = (data.flagStatus == 'flagged') ? 'addClass' : 'removeClass';
  $(data.link).parents('div.node, .views-row').eq(0)[method](class_names);
});

})(jQuery);

Put this code in a "flag-css.js" file (for example) in you theme's folder and add "scripts[] = flag-css.js" to your theme's .info file. Then clear Drupal's cache or else Drupal's theming system won't notice this new file.

What about Views?

When your view is styled as a table, list or unformatted, then 'node.tpl.php' isn't used and therefore the rows in the view won't carry our flag classes. To fix this, add the following preprocess functions to your template.php. Rename garland to the actual name of your theme. Don't forget to clear Drupal's cache.

#
# You must rename 'garland' to the actual name of your theme !!
#

function garland_preprocess_views_view_table(&$vars) {
  $view = $vars['view'];
  $rows = $vars['rows'];

  if ($view->base_table == 'node') {
    foreach ($rows as $id => $row) {
      $data = $view->result[$id];
      $flag_classes = implode(' ', _phptemplate_get_flag_classes('node', $data->nid));
      $vars['row_classes'][$id][] = $flag_classes;
      $vars['row_classes'][$id][] = 'views-row'; // For our JavaScript.
    }
  }
}

function garland_preprocess_views_view_unformatted(&$vars) {
  $view = $vars['view'];
  $rows = $vars['rows'];

  if ($view->base_table == 'node'
       && $view->display_handler->get_option('row_plugin') != 'node'
       && empty($view->style_plugin->options['grouping'])) {  // Note: we don't support Grouping.
    foreach ($rows as $id => $row) {
      $data = $view->result[$id];
      $flag_classes = implode(' ', _phptemplate_get_flag_classes('node', $data->nid));
      $vars['classes'][$id] .= ' ' . $flag_classes;
    }
  }
}

function garland_preprocess_views_view_list(&$vars) {
  garland_preprocess_views_view_unformatted($vars);
}

Note: when using the list or unformatted styles, the classes won't be added to the rows if you use Grouping in your view.

(This code was tested on both Views 2.x and Views 3.x.)

This code is complicated! Why don't you just put it in a module?

Indeed, the ideal place for this code is in a contrib module.

Help improve this page

Page status: Not set

You can: