ID Per Menu Item + "Active" css state.

nimzie - February 27, 2008 - 19:20

Hi,

For my main nav, I have buttons designed with IDs. There's an Active type as well. What I need to do is create the menu in Drupal, assign an ID to each menu item and be able to configure my "active" css state, too. Not so sure how to go about this.

Thanks for any advice.

Cheers

Adam

Sorry to re-post in my own

nimzie - March 4, 2008 - 01:50

Sorry to re-post in my own article. What I'm looking to do is have a menu I can make in Drupal. That menu has 5 items. Each has its own ID. Also, when one is clicked, it would have an Active state which I can tie to my CSS as well.

Is there a component or some snippets of code out there which can help me do this?

Thanks,

Adam

I'"ve figured out part of

nimzie - March 4, 2008 - 19:43

I'"ve figured out part of the problem. I've entered this function in my template.php

function phptemplate_menu_item($mid, $children = '', $leaf = TRUE) {
  return _phptemplate_callback('menu_item', array(
        'leaf' => $leaf,
        'mid' => $mid,
        'children' => $children
    ));
}

and this code in a menu_item.tpl.php file:

<?php
$link
= menu_item_link($mid);
if ((
drupal_get_normal_path($item['path']) == $_GET['q']) // if menu item path...
|| (drupal_is_front_page() && $item['path'] == '<front>')) { // or front page...
  
$active_class = ' active'; // set active class
} else { // otherwise...
  
$active_class = ''; // do nothing
}
// replace spaces with "_", and strip HTML
$css_id = str_replace(' ', '_', strip_tags($link));
$output = '<li id="'.$css_id.'" class="'. ($leaf ? 'leaf' : ($children ? 'expanded' : 'collapsed')) . $active_class .'">'. $link . $children ."</li>\n";
print
$output;
?>

This works perfectly when I click on one of my top menu items images. The active class is implemented as desired.

I would like to make these menu items "category" or taxonomy aware so when I'm on a page of X category, that category's corresponding top menu item is "active". As it is, when using the top menu only, the proper menu item stays "active" but as soon as I navigate other site items (which are categorized corresponding with the menu), the active state is not used.

Please advise.

Cheers,

Adam

Could what you describe also

Summit - March 12, 2008 - 10:58

Could what you describe also be used to highlight one menu item?
This need not be the active one? Just to give this menu item more look-and-feel?

greetings,
Martijn

Solution for Drupal 6

ronnqvist - April 13, 2008 - 16:22

Hi!

Unfortunately this doesn't work in Drupal 6 since _phptemplate_callback() has been dropped. However in Drupal 6 it's really easy to achieve the same thing, just put this in your template.php:

<?php
function phptemplate_menu_item($link, $has_children, $menu = '', $in_active_trail = FALSE, $extra_class = NULL) {
 
$class = ($menu ? 'expanded' : ($has_children ? 'collapsed' : 'leaf'));
  if (!empty(
$extra_class)) {
   
$class .= ' '. $extra_class;
  }
  if (
$in_active_trail) {
   
$class .= ' active-trail';
  }
#New line added to get unique ID:s for each menu item (id attribute also added to last line):
 
$css_id = str_replace(' ', '_', strip_tags($link));
  return
'<li class="'. $class .'" id="'. $css_id .'">'. $link . $menu ."</li>\n";
}
?>

What I'd really like to achieve though would be having the list items numbered, this I've also tried to achieve in other occasions. It'd be really nice to be able to obtain the iteration number ($i) of the loop repeatedly calling this function. A dirty solution would be to use global variables and add one each time that the function is called, but I know that's not the way to go. Please Drupal gurus, enlighten me on this. ;) (I wanted to achieve this also for gallery index pages, in order to be able to position the items. So this piece of knowledge would be useful for many purposes.)

cheers, Simon

So .. back to this one...

nimzie - April 24, 2008 - 12:53

OK - I've managed to get a better understanding of Drupal. Simply using a menu as Primary and another as secondary maintains the active link between subnav and top nav.

My issue: for each section (think top nav) of my site, I slide in a different subnav menu. I'm not sure how to get things to stay "active" in the primary nav when I am navigating through secondary nav items.

i.e. If I'm in the "people" section of my site - I show the people menu as subnav. Clicking on a person in the subnav should maintain the active state of the "people" nav item up top.

How do I maintain this when I'm using different blocks for different menus?

Me too.

scottatdrake - July 24, 2008 - 15:44

I'm trying to do the exact same thing. I am using blocks as a 3rd level subnavs. When you click on a third level link, the 2nd level of navigation disappears.

Did you ever figure out how keep the state on the 2nd level menus, nimzie? I'll let you know if I do!

I worked with coreyp_1

nimzie - August 11, 2008 - 13:51

I worked with coreyp_1 (thank you - you're a Drupal Deity) to come up with this solution.

For my main nav, I'm outputting this.

<?php
 
if ($primary_links):
     
// customization from drupal.org/node/140491
      // original code: print theme('links', $primary_links);
     
print customize_links($primary_links, array('class' => 'links primary-links'));
  endif;
?>

For the secondary nav, I've created a block with the following code. This block IS my subnav code:

<?php
print get_custom_menu('secondary', 2);
?>

I've got a new module which Corey developed for me. You can download it at:
http://shorelineconsulting.ca/upload/custom_menus.zip

Install it as a module and your block code will just "work".

To see this menu in action:
http://brighthost.ca/nsfa_main/

Drupal 6?

scottatdrake - August 13, 2008 - 15:59

It looks like this is for 5.x. I've started to convert it for Drupal 6.x but am having a hell of a time so far. (This is my first attempt to update a module.)

I can't help but wonder... Is there an easier way to do this in 6 with the revamped menu system?

I worked with coreyp_1

nimzie - August 11, 2008 - 14:08

I worked with coreyp_1 (thank you - you're a Drupal Deity) to come up with this solution.

For my main nav, I'm outputting this.

<?php
 
if ($primary_links):
     
// customization from drupal.org/node/140491
      // original code: print theme('links', $primary_links);
     
print customize_links($primary_links, array('class' => 'links primary-links'));
  endif;
?>

For the secondary nav, I've created a block with the following code. This block IS my subnav code:

<?php
print get_custom_menu('secondary', 2);
?>

I've got a new module which Corey developed for me. You can download it at:
http://shorelineconsulting.ca/upload/custom_menus.zip

Install it as a module and your block code will just "work".

To see this menu in action:
http://brighthost.ca/nsfa_main/

I tried to figure that out

dorian_ - July 17, 2008 - 14:12

I tried to figure that out for hours! Thanks a lot!!! Works perfect! No I can begin to set up my CSS!!

Drupal 6

shrenster - November 11, 2008 - 17:27

Ronnqvist

Does this solution work for custom menus (not using primary/secondary menus) in Drupal 6. I need to have the menu image highlighted for the current page. If this soultion provides me with the unique id/class for the page, then I can use it to style the menu image for each page.

regards
Shrenster

A better solution imho for Drupal 5

couzinhub - November 18, 2008 - 02:09

this doesn't require an additionnal menu-item.tpl.php and it creates nicer classes

In template.php :

<?php
/**
* Implementation of theme_menu_item().
* Add active class and custom id to current menu item links.
*/
function phptemplate_menu_item($mid, $children = '', $leaf = TRUE) {
 
$item = menu_get_item($mid); // get current menu item
  // decide whether to add the active class to this menu item
 
if ((drupal_get_normal_path($item['path']) == $_GET['q']) // if menu item path...
 
|| (drupal_is_front_page() && $item['path'] == '<front>')) { // or front page...
   
$active_class = ' active'; // set active class
 
} else { // otherwise...
   
$active_class = ''; // do nothing
 
}
 
$attribs = isset($item['description']) ?
array(
'title' => $item['description']) : array();
 
$replace = array(' ', '&');
 
$attribs['id'] = 'menu-'. str_replace($replace, '-', strtolower($item['title']));
  return
'
<li class="'
. ($leaf ? 'leaf' : ($children ? 'expanded' : 'collapsed')) .
$active_class .'" id="'. $attribs['id'] . '">' .
menu_item_link($mid) . $children ."</li>
\n"
;
}
?>

Also for drupal 6

couzinhub - November 18, 2008 - 08:32

An alternative of the code posted above, using id-safe of zen to turn every item to lower case, and only use class, because id is not safe markup. If two menu items have the same name, it would give two similar ids :

In template.php (without the php opening and closing) :

<?php
   
function phptemplate_menu_item($link, $has_children, $menu = '', $in_active_trail = FALSE, $extra_class = NULL) {
 
$class = ($menu ? 'expanded' : ($has_children ? 'collapsed' : 'leaf'));
  if (!empty(
$extra_class)) {
   
$class .= ' '. $extra_class;
  }
  if (
$in_active_trail) {
   
$class .= ' active-trail';
  }
#New line added to get unique classes for each menu item
 
$css_class = phptemplate_id_safe(str_replace(' ', '_', strip_tags($link)));
  return
'<li class="'. $class . ' ' . $css_class . '">' . $link . $menu ."</li>\n";
}


//   
//    Converts a string to a suitable html ID attribute.
//   
//     <a href="http://www.w3.org/TR/html4/struct/global.html#h-7.5.2" title="http://www.w3.org/TR/html4/struct/global.html#h-7.5.2" rel="nofollow">http://www.w3.org/TR/html4/struct/global.html#h-7.5.2</a> specifies what makes a
//     valid ID attribute in HTML. This function:
//   
//    - Ensure an ID starts with an alpha character by optionally adding an 'n'.
//    - Replaces any character except A-Z, numbers, and underscores with dashes.
//    - Converts entire string to lowercase.
//   
//    @param $string
//      The string
//    @return
//      The converted string
//   


function phptemplate_id_safe($string) {
 
// Replace with dashes anything that isn't A-Z, numbers, dashes, or underscores.
 
$string = strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string));
 
// If the first character is not a-z, add 'n' in front.
 
if (!ctype_lower($string{0})) { // Don't use ctype_alpha since its locale aware.
   
$string = 'id'. $string;
  }
  return
$string;
}
?>

This is exactly what I'm

LRHoek - December 4, 2008 - 16:45

This is exactly what I'm looking for as well. However, it seems that my template.php file is totally ignored. I'm trying to do this with my primary menu items. Does that require a different function?

If Drupal 6

couzinhub - December 6, 2008 - 00:44

If you are in drupal 6, you need to refresh the theme cache when you change the template.php. You can do this by just visiting the theme page. and validate without changing anything.
______________________________

www.couzinhub.com

You are right!

LRHoek - December 8, 2008 - 10:22

You are right!

I was under the impression that I had already done this, but that nothing had changed. Thanks a lot!

If you want to avoid having

couzinhub - December 8, 2008 - 18:42

If you want to avoid having to do that every time you change the template.php, you can either use the devel module, I think there is a function that clears the cache on demand. Also, by using the ZEN theme, you can activate 'clear cache on each page reload'. It's just a bit demanding on memory so make sure you turn it off after development. Basic 2.1 (not released yet) also have that function.

You can also visit the page admin/settings/performance. A button clears the cache.

______________________________

www.couzinhub.com

Wow -- deep gratitude

Kyle Skrinak - December 18, 2008 - 04:12

This is most excellent -- AND standards compliant. Look away from using id's, all.

This is a great addition,

torgosPizza - October 26, 2009 - 22:23

This is a great addition, thanks! I'm still baffled that it's not included in the Drupal core menu creation process...

Help fund more Ubercart and Drupal development! Donate!

You can find this code in

couzinhub - October 27, 2009 - 01:54

You can find this code in some base theme, I included an improved version of it in Basic

____________________________________________________________

en: www.couzinhub.com
fr: www.couzinhub.com

now with 'parents'

robotoverlord - December 8, 2008 - 23:16

I've combined the code above with another snippet I found so that now the snippet adds an active class to li elements and a elements, custom id to menu items links PLUS - a 'parent' class to menu items further up the food chain. I didn't write any of it, just shuffled it around a bit.

/**
* Implementation of theme_menu_item().
* Add parent class to parent items, active class to li elements and a elements, custom id to current menu item links.
*/
function phptemplate_menu_item($mid, $children = '', $leaf = TRUE) {
  $item = menu_get_item($mid); // get current menu item
  // decide whether to add the active class to this menu item
  if ((drupal_get_normal_path($item['path']) == $_GET['q']) // if menu item path...
  || (drupal_is_front_page() && $item['path'] == '<front>')) { // or front page...
    $active_class = ' active'; // set active class
  } else { // otherwise...
    $active_class = ''; // do nothing
  }
  $attribs = isset($item['description']) ?
array('title' => $item['description']) : array();
  $replace = array(' ', '&');
  $attribs['id'] = 'menu-'. str_replace($replace, '-', strtolower($item['title']));
  return
'<li class="'. ($leaf ? 'leaf' : ($children ? 'expanded' : 'collapsed')) . (menu_in_active_trail($mid) ? ' parent' : '') . $active_class .'" id="'. $attribs['id'] . '">' . menu_item_link($mid) . $children ."</li>\n";
}

[[[ ALL HAIL THE ALMIGHTY GOOGLEBOT ]]]

 
 

Drupal is a registered trademark of Dries Buytaert.