Community Documentation

Add "first" and "last" classes on blocks

Last updated March 4, 2010. Created by marcvangend on August 9, 2008.
Edited by droofan, lisarex, bryan kennedy. Log in to edit this page.

Sometimes, the first or last block in a region needs to be styled different than the rest. To get 'first' and 'last' classes on the first and last block in a region, you need to override two theme functions: theme_block() and theme_blocks().

1) Override theme_blocks()

<?php
function phptemplate_blocks($region) {
 
$output = '';

  if (
$list = block_list($region)) {
   
$blockcounter = 1; // there is at least one block in this region
   
foreach ($list as $key => $block) {
     
// $key == <i>module</i>_<i>delta</i>
     
$block->extraclass = ''; // add the 'extracclass' key to the $block object
     
if ($blockcounter == 1){ // is this the first block in this region?
       
$block->extraclass .= ' first'
      }
      if (
$blockcounter == count($list)){ // is this the last block in this region?
       
$block->extraclass .= ' last';
      }
     
$output .= theme('block', $block);
     
$blockcounter++;
    }
  }

 
// Add any content assigned to this region through drupal_set_content() calls.
 
$output .= drupal_get_content($region);

  return
$output;
}
?>

2) Override theme_block()

<?php
function phptemplate_block($block) {
 
$output  = "<div class=\"block block-$block->module $block->extraclass\" id=\"block-$block->module-$block->delta\">\n"; // in this line, the extraclass value is added as class
 
$output .= " <h2 class=\"title\">$block->subject</h2>\n";
 
$output .= " <div class=\"content\">$block->content</div>\n";
 
$output .= "</div>\n";
  return
$output;
}
?>

That's it! You may need to clear cache before you see the change.

Comments

already overriding _block function phptemplate.engine

Thank you very much for the function... I was able to apply it to my theme.

I'm not entirely sure why this was, but my phptemplate was already overriding the theme_block function with a phptemplate_block() function in the phptemplate.engine file.

Once I thought about it it made sense, however, as I am using a block.tpl.php file...so I got this function to work by adding the phptemplate_blocks() function to my template.php file, then changing

class="block <?php print $block_classes; ?>

to

class="block <?php print $block_classes.$block->extraclass; ?>

in my block.tpl.php file. I'm kind of curious how I would override other theme functions that have already been overridden in the phptemplate.engine file as this was the approach I first attempted to no avail, but I'm just happy I got this to work.

Drupal 6 + Zen Theme

I had to do something different to add "first" and "last" classes to my blocks. The site I was working on is using Drupal 6 and its custom theme is based on Zen.

To add "first" and "last" classes to blocks in this situation, I had to add the following in my theme's template.php:

<?php
function themename_preprocess_block(&$vars, $hook)
{
    static
$counts;
    if(!isset(
$counts)) $counts = array();
   
$region = $vars['block']->region;
    if(!isset(
$counts[$region])) $counts[$region] = count(block_list($region));
   
$count = $counts[$region];
   
$extremity = '';
    if(
$vars['id'] == 1) $extremity = 'first';
    if(
$vars['id'] == $count) $extremity = 'last';
   
$vars['classes'] .= $extremity != '' ? ' ' . $extremity : '';
}
?>

My preferred D6 method

For Drupal 6, I prefer this method (basically a rewrite of the D5 method above, the code goes in template.php):

<?php
function themename_blocks($region) {
 
$output = '';

  if (
$list = block_list($region)) {
   
$blockcounter = 1;
    foreach (
$list as $key => $block) {
     
$block->extraclass = '';
     
$block->extraclass .= ( $blockcounter == 1 ? ' block-first' : '' ); 
     
$block->extraclass .= ( $blockcounter == count($list) ? ' block-last' : '' ); 
     
$output .= theme('block', $block);
     
$i++;
    }
  }

 
// Add any content assigned to this region through drupal_set_content() calls.
 
$output .= drupal_get_content($region);

  return
$output;
}

function
themename_preprocess_block(&$vars){
 
$vars['classes'] .= $vars['block']->extraclass;
}
?>

This method is (at least in theory) better for performance: block_list() is already called in themename_blocks(), so I don't want to call it again in themename_preprocess_block().

I noticed a typo in the above

I noticed a typo in the above code when I tried to implement it. It should be as follows:

<?php
function themename_blocks($region) {
 
$output = '';

  if (
$list = block_list($region)) {
   
$blockcounter = 1;
    foreach (
$list as $key => $block) {
     
$block->extraclass = '';
     
$block->extraclass .= ( $blockcounter == 1 ? ' block-first' : '' );
     
$block->extraclass .= ( $blockcounter == count($list) ? ' block-last' : '' );
     
$output .= theme('block', $block);
     
$blockcounter++;
    }
  }

 
// Add any content assigned to this region through drupal_set_content() calls.
 
$output .= drupal_get_content($region);

  return
$output;
}

function
themename_preprocess_block(&$vars){
 
$vars['classes'] .= $vars['block']->extraclass;
}
?>

For my own uses I only needed to mark the last block in a class, so I addapted this as follows:

<?php
function csc_zen_blocks($region) {
 
$output = '';

  if (
$list = block_list($region)) {
   
$blockcounter = 1;
    foreach (
$list as $key => $block) {
     
$block->is_last = ( $blockcounter == count($list) ? true : false );
     
$blockcounter++;
     
$output .= theme('block', $block);
    }
  }

 
// Add any content assigned to this region through drupal_set_content() calls.
 
$output .= drupal_get_content($region);

  return
$output;
}

function
csc_zen_preprocess_block(&$vars, $hook) {
  if (
$vars['block']->is_last) :
   
$vars['classes'] .= ' region-last';
  endif;
}
?>

––
Jeremy Stoller
Senior Graphic Artist
California Science Center

Hey Jeremy, Thank you, and

Hey Jeremy,

Thank you, and the rest of the folks on this post for this snippet. I just added it to my customized Basic theme. I did notice a small issue, however. B/c of the way that you have the counter setup, if there's only one block in the region, it adds both the first and last classes. I'm not sure if that's intentional - you could argue it either way. Just something for folks to keep in mind.

Cheers,
Sean
ThinkShout.com

Sean Larkin
ThinkShout

For some reason i cant get

For some reason i cant get this to work, does it only work with the zen theme?

Nevermind sampeckham's post

Nevermind sampeckham's post sorted it out for me. Thanks

Drupal 7 Version

Thanks, I modified your code for Drupal 7:

<?php
   
static $counts;
    if(!isset(
$counts)) $counts = array();
   
$region = $vars['block']->region;
    if(!isset(
$counts[$region])) $counts[$region] = count(block_list($region));
   
$count = $counts[$region];
   
$extremity = '';
    if(
$vars['block_id'] == 1) $extremity = 'first';
    if(
$vars['block_id'] == $count) $extremity = 'last';
   
array_push($vars['classes_array'], $extremity);   
?>

Can't get it to work

This is just the snippet I need, yet I can't get it to work on my site.

I've cleared cache and can tell it's reading the template.php as it kills the site when there is an error in there.

Using D6, added themename and the page loads fine but doesn't add the 'first' or 'last' classes. I'm running in a subtheme (from ninesixty) and this is the only function in my themes template file.

Any ideas? Many thanks.

[I've tried both remi and jstoller's version]

Fixed!

After a staring at this for a while and re-reading the whole thread a few times I realised I had my own block.tpl.php template in my theme, and therefore had to add the following to the block.tpl.php file:

<?php print $block->extraclass ?>

As noted in the second post by vood002, I had half dismissed that as it was D5 based, but it obvioulsy it applies to D6 as well! Happy days - great snippet. Thanks all.

D6 with Zen 2.0 - first last block class

For use within a subtheme of zen 2.0 you can use the following code in templte.php:

<?php

function YOURTHEMENAME_blocks($region) {
 
$output = '';

  if (
$list = block_list($region)) {
   
$blockcounter = 1;
    foreach (
$list as $key => $block) {
     
$block->extraclass = '';
     
$block->extraclass .= ( $blockcounter == 1 ? ' block-first' : '' );
     
$block->extraclass .= ( $blockcounter == count($list) ? ' block-last' : '' );
     
$output .= theme('block', $block);
     
$blockcounter++;
    }
  }
 
 
// Add any content assigned to this region through drupal_set_content() calls.
 
$output .= drupal_get_content($region);
 
 
$elements['#children'] = $output;
 
$elements['#region'] = $region;

  return
$output ? theme('region', $elements) : '';
}

function
YOURTHEMENAME_preprocess_block(&$vars, $hook) {
   
$vars['classes_array'][] .= $vars['block']->extraclass;
}
?>

Do you have a D7 version of

Do you have a D7 version of this?

not sure if this is the right area to comment but

Hi! I just want to ask... is it safe to remove

<?php
print $classes;
?>
from block.tpl.php?
more details here...
http://groups.drupal.org/node/152479

Thanks a lot!

You can teach a student a lesson for a day; but if you can teach him to learn by creating curiosity, he will continue the learning process as long as he lives. ~Clay P. Bedford
www.magazinesnation.com | www.magazinesmags.com/blog

A new forum topic would be a

A new forum topic would be a better place for this question, but now that you're here...
It's fine to remove that code from the template. If you don't want those classes to be printed, you can leave them out. That said, removing the classes might break any css and javascript that depends on it. If you want to remove the classes because of the css styles applied to it, then changing the css would be a better solution.
I hope this answers your question - if not, please open a new forum topic.

Thanks!

Hi! marcvangend. I see so it's better to keep it. But since it's not yet used in any within the theme, what can I placed in css to at least provide it with some value but still won't affect the current styling? Any piece of code that you can impart to me. Thanks!

You can teach a student a lesson for a day; but if you can teach him to learn by creating curiosity, he will continue the learning process as long as he lives. ~Clay P. Bedford
www.magazinesnation.com | www.magazinesmags.com/blog

.

I think I clearly asked you to open a new forum topic if you have more questions. This page is for documentation.

Thanks!

Hi! marcvangend. I see so it's better to keep it. But since it's not yet used in any within the theme, what can I placed in css to at least provide it with some value but still won't affect the current styling? Any piece of code that you can impart to me. Thanks!

You can teach a student a lesson for a day; but if you can teach him to learn by creating curiosity, he will continue the learning process as long as he lives. ~Clay P. Bedford
www.magazinesnation.com | www.magazinesmags.com/blog

Drupal 7 one-liner for 'first'

I only need 'first' so all I do is add this to my override of theme_preprocess_block :

<?php
if( $variables['block_id'] == 1 ) $variables['classes_array'][] = 'block-first';
?>

Drupal 7 Method From Zen Theme

I found this method pretty easy as it uses the $classes variable already in the the block.tpl.php.

I pulled it from the zen theme.

<?php
function YOURTHEMENAME_preprocess_block(&$variables, $hook) {
 
// Classes describing the position of the block within the region.
 
if ($variables['block_id'] == 1) {
   
$variables['classes_array'][] = 'first';
  }
 
// The last_in_region property is set in zen_page_alter().
 
if (isset($variables['block']->last_in_region)) {
   
$variables['classes_array'][] = 'last';
  }
 
$variables['classes_array'][] = $variables['block_zebra'];

 
$variables['title_attributes_array']['class'][] = 'block-title';
}
?>

<?php
function YOURTHEMENAME_page_alter(&$page) {
 
// Look in each visible region for blocks.
 
foreach (system_region_list($GLOBALS['theme'], REGIONS_VISIBLE) as $region => $name) {
    if (!empty(
$page[$region])) {
     
// Find the last block in the region.
     
$blocks = array_reverse(element_children($page[$region]));
      while (
$blocks && !isset($page[$region][$blocks[0]]['#block'])) {
       
array_shift($blocks);
      }
      if (
$blocks) {
       
$page[$region][$blocks[0]]['#block']->last_in_region = TRUE;
      }
    }
  }
}
?>

Add both of these functions to your theme and clear your cache. You will notice the new classes in your blocks.

Thank you. Finally some D7

Thank you. Finally some D7 code for this. Didn't notice the zebra classes at first but that's awesome too. Thanks again.

Another approach to drupal 7

Without going with hook_page_alter:

<?php
 
// Simplify variables output to .tpl
 
$variables['attributes_array']['class'] = &$variables['classes_array'];
 
$variables['attributes_array']['id'] = $variables['block_html_id'];

 
// Increment block classes
 
$variables['attributes_array']['class'][] = 'block-' . $variables['block_id'];
 
 
// More classes here.......

  // Classes describing the position of the block within the region.
 
$variables['attributes_array']['class'][] = ($variables['block_id'] == 1) ? 'block-first' : '';
 
// when using context, need a modified block_list to merge with context
  // $variables['attributes_array']['class'][] = ($variables['block_id'] == count(mytheme_block_list($variables['block']->region))) ? 'block-last' : '';
  // We have no context.module installed
 
$variables['attributes_array']['class'][] = ($variables['block_id'] == count(block_list($variables['block']->region))) ? 'block-last' : '';

 
// Add odd/even zebra classes into the array of classes
 
$variables['attributes_array']['class'][] = drupal_html_class('block-' . $variables['zebra']);
 
$variables['attributes_array']['class'][] = 'clearfix';
?>

Then in block.tpl.php:

<div <?php print $attributes ?>>
  <div class="block-inner">
   ............etc

love, light n laughter

Page status

No known problems

Log in to edit this page

About this page

Drupal version
Drupal 5.x
Audience
Themers

Theming Guide

Drupal’s online documentation is © 2000-2012 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.
nobody click here