This adds unique IDs (using the link's text) to any menu items so you can style them uniquely in your CSS.

In your template.php:

<?php
/**
* Theme override for theme_menu_item()
*/
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';
  }
  
  // Add unique identifier
  static $item_id = 0;
  $item_id += 1;
  $id .= ' ' . 'menu-item-custom-id-' . $item_id;
  // Add semi-unique class
  $class .= ' ' . preg_replace("/[^a-zA-Z0-9]/", "", strip_tags($link));
  
  return '<li class="'. $class .'" id="' . $id . '">'. $link . $menu ."</li>\n";
}
?>

Comments

GreenSkunk’s picture

If you are using a Zen-based theme you can use the Zen function zen_id_safe
replace line
$id = preg_replace("/[^a-zA-Z0-9]/", "", strip_tags($link));
with
$id = zen_id_safe(strip_tags($link));

"It is a damn poor day when you don't learn something!" - Mr. Jones, teacher

KrisBulman’s picture

Greenskunk,

Are you referring to the latest release of Zen? I attempted to use zen_id_safe (using this code in my subthemes template.php) and immediately got a the white screen of death. Is there something I'm missing?

<?php
/**
* Theme override for theme_menu_item()
*/
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';
  }
 
  // Add unique identifier
  static $item_id = 0;
  $item_id += 1;
  $id .= '' . 'menu-item-custom-id-' . $item_id;
  // Add semi-unique class
  $class .= ' ' . zen_id_safe(strip_tags($link));
 
  return '<li class="'. $class .'" id="' . $id . '">'. $link . $menu ."</li>\n";
}
?>
njacobson’s picture

So, I need to add a unique class to each secondary menu ul. If that doesn't make sense, here's an example

I have one menu. My menu has 4 primary items and about 6 secondary items under each primary.
My page design shows all primary items and the expanded secondary menu for that section.

I need to be able to style (through css) the secondary menu items differently for each section. So for sectionA, the secondary menu has a margin of 100px. For sectionB, the secondary menu has a margin of 300px and on...

There is currently only an id="secondary-links" on my ul tag. I need to add a unique class or id for each secondary ul based on the primary link.

Using the genesis theme starter if that makes a difference. Help appreciated!

pawel.traczynski’s picture

Hi, Njacobson.

I don't know what exacltly are you trying to achieve but you can use jQuery to do what you want.
The jQuery code should by added to your script.js file, in your theme directory.

First let's select all the 'li' elements of the menu, but without selecting their children 'li' elements (only top-level li elements).
Also for each selected 'li' tag select it's first 'a' child tag, and check it's value of "title" parameter (configurable via admin interface Admin -> Build -> Menus).

We save the 'title' value in a variable parentATitle (and optionally we remove the title parameter from the 'a' tag).

$(document).ready(function() {
  $('#secondary-links ul li').not($('#secondary-links ul li li').each(function() {
    var parentLi = $(this);
    var parentATitle = $(parentLi + ' a:first').attr('title').removeAttr('title');
  });
});

Now we will add more code, which will use the parent 'a' tag value to add a new class to the child 'ul' tag.

$(document).ready(function() {
  $('#secondary-links ul li').not($('#secondary-links ul li li').each(function() {
    var parentLi = $(this);
    var parentATitle = $(parentLi + ' a:first').attr('title').removeAttr('title');

    $(parentLi + ' ul').not(parentLi + ' ul ul').addClass('class-' + parentATitle);
  });
});

So now if you have a structure like:

<ul id="secondary-links">
  <li>
    <a href="" title="main">X</a>
    <u>
        <li><a href="">A</a></li>
        <li><a href="">B</a></li>
        <li><a href="">C</a></li>
    </ul>
  </li>
  <li>
    <a href="" title="second">Y</a>
    <u>
        <li><a href="">D</a></li>
        <li><a href="">E</a></li>
        <li><a href="">F</a></li>
    </ul>
  </li>
</ul>

and you use the above jQuery code, it will first select the X link, read it's title and apply this title as a part of a new class for the 'ul' containing link A, B and C (it's class will be "class-main"). The same will happen for the Y link. So the class of the 'ul' containg link D, E, F will be "class-second".

I hope it will help you somehow. jQuery is a nice javascript library :-)

Cheers!

I am a Drupal developer and astrophotographer.
https://www.astrobin.com/users/pawel.traczynski/

scotthoff’s picture

This might be an easier way to understand the original comment. You set your id to equal

zen_id_safe(strip_tags($link['title']));	

If you just use $link, it returns "array"

Toongenius’s picture

I've added it to my template file, but my menus don't change at all. I don't get it! Am I doing something wrong- this is so frustrating!

EDIT: all good- I had to flush my cache and run cron.

polymathnyc’s picture

I'm not having any luck with this in Zen in Drupal 6.19. I have also tried in Garland, and its not working there. I have of course flushed all caches and run cron each time. Is there something I could be doing wrong when adding this code to template.php? It's not that the ID function simply isn't working because I'm not seeing any change at all in the menu items (ie. I'm not seeing id="").

A5lex’s picture

How it can be used only for Primary links?

teflo’s picture

Exactly what I need! Work perfectly!

d.sibaud’s picture

never mind

-------
Everytime you hack the core somebody die.
Please don't do it.

http://www.tourtools.it
http://www.realizzazione-siti-web-drupal.it

heyyo’s picture

Any guide lines for Drupal 7 ?

Anonymous’s picture

I'm curious too!

CKIDOW’s picture

Me as well!

MfG

CKIDOW

Anonymous’s picture

I posted a topic about this, especially for Drupal 7, and got a solution.

doublejosh’s picture

Menu items IDs seem to change on me.
Is this indicative of some type of menu failure or are they not a stable thing to rely on for styling icons etc?

Priyanka Singh’s picture

menu_attribute module can be used here to give unique id to each menu item.

moonshdw8’s picture

Just confirming that menu_attributes does this really well and even opened up some new possibilities I hadn't thought of before.

paultrotter50’s picture

Hi,

I have done something similar to the OP to create custom menu items in my Drupal 6 menu. I required quite an extensive customised html/css output in order style the menu in a reasonably complex way. I have included the code below in case it is of value to anyone else.

What I now need to achieve is to allow the customised menu_item code to distinguish which menu the menu_item is from, so that the output is different for the main navigation and my custom menus. I expect I could achieve this by using an if statement in the function phptemplate_menu_item e.g.

function phptemplate_menu_item (...){
if ($menu_name == 'primary-links')
{DO ABC}
else
{DO XYZ}
}

However I don't think the $menu_name is currently being passed to the menu_item? So the first thing I may need to do is work out how to pass the menu_name to the phptemplate_menu_item. I been searching for quite a while for possible solutions to this, so any input or areas I should read up on would be appreciated. Thanks!

I have included my current code below for anyone who may benefit from it:

function phptemplate_menu_item($link, $has_children, $menu_name, $menu = '', $in_active_trail = FALSE, $extra_class = NULL) {
  
  $class = ($menu ? 'no_wrap' : ($has_children ? 'collapsed' : 'li_wrap'));
  if (!empty($extra_class)) {
    $class .= ' '. $extra_class;
  }
  if ($in_active_trail) {
    $class .= ' active-trail';
  }
  if (!empty($link)) {
	/* The following section gives the list items unique classes based on their link text - note how spaces and sepcial chars are removed */
	// remove all HTML tags and make everything lowercase
	$css_id = strtolower(strip_tags($link));
	// remove colons and anything past colons
	if (strpos($css_id, ':')) $css_id = substr ($css_id, 0, strpos($css_id, ':'));
	// Preserve alphanumerics, everything else goes away
	$pattern = '/[^a-z]+/ ';
	$css_id = preg_replace($pattern, '', $css_id);
	$class .= ' '. $css_id;
	}
	// the following code returns the menu item formatted in a different fashion depending on the class of the item. The first one is for items with a class of none - ie the space li at end of menu
	if (strstr($class, 'none')) {
		  return '<li class="'. $class . $menu_name . ' main"></span></span></li>';
	}
  	if (strstr($class, 'li_wrap')) {
		  return '<li class="'. $class . $menu_name . ' main"><span class="wrapped">'. $link . $menu ."<span class='indicator'></span></li>\n";
	}
	if (strstr($class, 'no_wrap')) {
		  return '<li class="'. $class . ' main">'. $link ."<span class='indicator'></span><span class='menu_box'><span class='menu_box_inner'>". $menu ."</span></span></li>\n";
	}
	
}
nirbhasa’s picture

This code was written when D8 was at alpha2 so I can't 100% guarantee this won't change. I also added a bit to reset the static variable when printing the last link, so that the class starts at 1 for each variable.

use Drupal\Core\Template\Attribute; 

function MYTHEME_menu_link($variables) {
  
  $element = $variables['element'];

  static $item_id = 0;
  $element['#attributes']['class'][] = 'menu-item-custom-id' . (++$item_id);
  $sub_menu = $element['#below'] ? drupal_render($element['#below']) : '';

  $output = l($element['#title'], $element['#href'], $element['#localized_options']);
  if(in_array('last', $element['#attributes']['class'])) {
     $item_id = 0; 
  } 

  return '<li' . new Attribute($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
 
}