Add <span> tags to Primary links for Sliding Door Tabs

In the upcoming series of Genesis video tutorials I will be presenting a segment on how to build sliding door tabs for Drupal's Primary links.

As you may know to get the full hover effect working in Microsoft's venerable Internet Explorer web browser we need to wrap the anchor text in <span> tags, since IE does not support the :hover pseudo class on anything but an anchor, bless its little heart.

Now, the standard Drupal "fix" for this is to override the theme_links function. I'll detail that here, but first I want to explore another idea I hit on recently - using jQuery to get the job done.

Do it with jQuery

Recently I was messing about with jQuery for dynamically adding and removing classes to Nodequeue links to support icons, eventually this lead to a patch for Nodequeue, but I haven't got around to fixing that up since I hit a few patch generation snags.

So with jQuery on my mind I started with a set of sliding door tabs for a theme I was working on at the time. When it came time to paste in the modified theme_links function (we all have one in our snippets lib don't we?), I though, man I hate this gigantic function sitting in my template.php file - can't I do this in like one or two lines of jQuery and be done?

Being a relative jQuery noob (to be frank), I headed over to visual jQuery to track down the necessities - pretty quickly I found wrapInner...

wrapInner(html)

This wrapping process is most useful for injecting additional structure into a document, without ruining the original semantic qualities of a document. This works by going through the first element provided (which is generated, on the fly, from the provided HTML) and finds the deepest ancestor element within its structure -- it is that element that will enwrap everything else.

Ok, that sounds like it'll get the job done. Now I just need to tell it what to insert and around which elements. I was building a Genesis subtheme and this theme prints an ID in the primary links wrapper <div>, namely the highly original id="primary". But we need to get the tags around that anchor text so I need to specifically target the anchor:

// Wrap span tags around the anchor text in the primary menu.
$(document).ready(function(){
  $("#primary li a")
  .wrapInner("<span>" + "</span>");
});

The "easy" way to get this working is to place this code in your script.js file inside your theme folder and then clear the theme registry (Performance settings > Clear cache data). The script.js is one of the assumed default values for Drupal 6 themes.

The nice thing about this method is that it takes 2 minutes to implement and does not affect Secondary links. The downside - if the user has JavaScript turned off in their browser it will degrade rather disgracefully as only one end of the tab will display. But hey, one assumes users with JS off are already having a pretty dull and broken internet experience these days (not really a valid excuse but I can live with it), and the links continue to work just fine so at least they can still navigate the site.

Over riden' the theme_links function

This is the traditional way to get the job done - its clean and follows the standard Drupal methodology for overriding themeable output.

All we need to do is grab the theme links function, copy it to our template.php file and make the necessary changes (jamming in those darn <span>) tags:

<?php
/**
 * Override the theme_links function
 *
 * We use this to insert <span></span> tags around anchor text in the
 * primary and secondary links. We need these to support Internet Explorer
 * when building sliding door tabs with hover effects.
 */
function MYTHEME_links($links, $attributes =  array('class' => 'links')) {
 
$output = '';
  if (
count($links) > 0) {
   
$output = '<ul'. drupal_attributes($attributes) .'>';

   
$num_links = count($links);
   
$i = 1;

    foreach (
$links as $key => $link) {
     
$class = $key;

     
// Add first, last and active classes to the list of links to help out themers.
     
if ($i == 1) {
       
$class .= ' first';
      }
      if (
$i == $num_links) {
       
$class .= ' last';
      }
      if (isset(
$link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))) {
       
$class .= ' active';
      }

     
$output .= '<li'. drupal_attributes(array('class' => $class)) .'>';
     
     
// wrap <span>'s around the anchor text
     
if (isset($link['href'])) {
       
$link['title'] = '<span>' . check_plain($link['title']) . '</span>';
       
$link['html'] = TRUE;    
       
// Pass in $link as $options, they share the same keys.
       
$output .= l($link['title'], $link['href'], $link);      
      }
      else if (!empty(
$link['title'])) {
       
// Some links are actually not links, but we wrap these in <span> for adding title and class attributes
       
if (empty($link['html'])) {
         
$link['title'] = check_plain($link['title']);
        }
       
$span_attributes = '';
        if (isset(
$link['attributes'])) {
         
$span_attributes = drupal_attributes($link['attributes']);
        }
       
$output .= '<span'. $span_attributes .'>'. $link['title'] .'</span>';
      }

     
$i++;
     
$output .= "</li>\n";
    }

   
$output .= '</ul>';
  }
  return
$output;
}
?>

Of course now you need to write some CSS, build a few tabs and you're away. Look out for the upcoming tutorial on that part of the story.

Last updated 29th July, 2009 - 8:07pm

Authored by Jeff Burnz on