I have a menu with sub-items:
+ a
-- a
-- b
- b
- c
I`m trying to print it with menu_tree()
And I have a problem which I can`t solve anyhow - how to assign to the child

    custom class?
    For example, now I have:
<ul class='menu'>
 <li>
  <a>first</a>
  <ul class='menu'>
   <li>
    <a>sub-item</a>
   </li>
  </ul>
 </li>
</ul>

And I want to make it:

<ul class='menu'>
 <li>
  <a>first</a>
  <ul class='sub'>
   <li>
    <a>sub-item</a>
   </li>
  </ul>
 </li>
</ul>

Any ideas?

Comments

edrupal’s picture

Don't know how to add a class to the 'ul' element but if you can do your styling with a class added to the 'li' sub menu elements this may help. Assuming Drupal 7

1. Copy the theme_menu_link function in its entirety from /includes/menu.inc to your themes template.php file.
2. Replace the word 'theme' with the name of your theme (e.g. my_custom_theme_menu_link)
3. Add the line $element['#below'][key($element['#below'])]['#attributes']['class'][] = 'my_sub_class'; to the if ($element['#below']) section

An example below:

function my_custom_theme_menu_link(array $variables) {
  $element = $variables['element'];
  $sub_menu = '';

  if ($element['#below']) {
    <-- the next line has been added to the function  --->  
    $element['#below'][key($element['#below'])]['#attributes']['class'][] = 'my_sub_class';

    $sub_menu = drupal_render($element['#below']);
  }
  $output = l($element['#title'], $element['#href'], $element['#localized_options']);
  return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}

This will add the class "my_sub_class" to all 'li' elements that are part of the sub menu. Hope this helps.

Ed

sheena_d’s picture

You could also do this with the Menu Attributes module

http://drupal.org/project/menu_attributes

tchopshop’s picture

I'm pretty sure you can only target a and li elements (with a patch), but not the ul element.

Elena Reeves-Walker
Tchopshop Media
tchopshop.com

devtherock’s picture

HI

Why you are looking for different class in submenu's UL element ?

TMWagner’s picture

What difference does it make? Either you know how to do it, or you don't.

If you must have a reason, one - for example is leveraging something like "slimmenu". It requires a class to be present in the UL

emilorol’s picture

After a couple of hours searching I found the template_menu_tree function and after reading about it and printing the value of the $variables variable I had the idea to add an unique keyword and then look for it with a simple match. If the match was true then I will add the variation to the keyword like from "menu" to "sub-menu".

<?php
function YOUR_THEME_menu_tree($variables) {
  if (preg_match("/\bKEYWORD-submenu\b/i", $variables['tree'])){
    return '<ul class="menu KEYWORD-menu">' . $variables['tree'] . '</ul>';
  } else {
    return '<ul class="menu KEYWORD-submenu">' . $variables['tree'] . '</ul>';
  }
}
?>

Thank you,
Emil

Emil Orol
"In the computer business you’re either a one or a zero and I am determined never to be zero."

timwhelanatl’s picture

@emilacosta

I am not a developer and would love it if you would be willing to explain this a little bit. I do not know who to add a keyword to the second

    tag for the code above to work and to switch it to sub-menu.

    Any ideas?
    Many Thanks
    Tim

emilorol’s picture

@timwhelanatl

OK, the code I shared you will add it in your template.php file and it is the use of the "theme_menu_tree" function and you will replace theme with your theme name. I used on Drupal 7, but the function also works on Drupal 6.

What the "theme_menu_tree" does is to return the menus in their "UL" tags with a basic class call "menu", so I figure out that if I added by own classes there I could also be able to add different classes by different levels of the menu. In my case I just have two levels, but you can tweak the code to make it work with an infinite number of levels.

What I mean by "KEYWORD" is a word that will make sense for you, in my case I added "alpha" so that the top menu class will be "alpha-menu" (KEYWORD-menu) and the second level menu will be "alpha-submenu" (KEYWORD-submenu)

Visual aid:

<ul class="menu alpha-menu">
  <li>item 1</li>
  <li>item 2</li>
  <li>
    <ul class="menu alpha-submenu">
      <li>item 1</li>
      <li>item 2</li>
      <li>item 3</li>
      <li>item 4</li>
    </ul>
  </li>
</ul>

Emil Orol
"In the computer business you’re either a one or a zero and I am determined never to be zero."

timwhelanatl’s picture

I think I am getting something wrong. I placed the code into the template file, added my theme name, and switch KEYWORD with alpha and both ul menus changed to the same thing.

I think my issue is that these are two menu blocks and I am probably thinking about this all wrong. Would what you have work with a tweak to the code below? I also tried a jQuery function but I am not getting that to work either.

jQuery

 $(function() {
	    $("#block-menu-menu-about-sub-menu ul").toggleClass('menu sub-menu')
	 });

php

<div id="block-menu-block-1" class="block block-menu-block block-1 block-menu-block-1 odd block-without-title">
<div class="block-inner clearfix">
<div class="content clearfix">
<div class="menu-block-wrapper menu-block-1 menu-name-main-menu parent-mlid-0 menu-level-1">
<ul class="menu alpha-submenu">
     <li class="first leaf menu-mlid-263">
     <li class="leaf has-children active-trail active menu-mlid-481">
     <li class="leaf menu-mlid-482">
     <li class="leaf menu-mlid-483">
     <li class="leaf menu-mlid-502">
     <li class="leaf menu-mlid-485">
     <li class="leaf menu-mlid-484">
     <li class="last leaf menu-mlid-503">
<select>
</ul>
</div>
</div>
</div>
</div>
<div id="block-menu-menu-about-sub-menu" class="block block-menu block-menu-about-sub-menu block-menu-menu-about-sub-menu even block-without-title">
<div class="block-inner clearfix">
<div class="content clearfix">
<ul class="menu alpha-submenu">
     <li id="epdn" class="first leaf">
     <li id="ceismc" class="leaf">
     <li id="learn" class="leaf">
     <li id="orbit" class="last leaf">
<select>
</ul>
</div>
</div>
</div>

I did not try these at the same time.

Thanks again for you patience and help!
Tim

emilorol’s picture

Hi Tim,

For what I can understand in your code you have two blocks and two independent menus from each other and that is different from what I was explaining of changing the class for a sub menu of a menu.

My code

  <ul class="menu alpha-menu">
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
    <li>
      <ul class="menu alpha-submenu">
        <li>item 1</li>
        <li>item 2</li>
        <li>item 3</li>
      </ul>
    </li>
  </ul>

Your code

<div id="block1" class="block classes">
  <ul class="menu alpha-menu">
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
  </ul>
</div>

<div id="block2" class="block classes">
  <ul class="menu alpha-menu">
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
  </ul>
</div>

Can you explain me what are you trying to do?

Emil Orol
"In the computer business you’re either a one or a zero and I am determined never to be zero."

timwhelanatl’s picture

I am using Mega Menu but have the sub-menu in different blocks for control. I am building a responsive website where on a mobile device the menus will turn into a drop down versus having all the main menu and sub menu taking up the screen- mimicking this site... www.utk.edu ... (in browser reduce width to see what it will look like on a phone). However, I need each menu to have a different description hence I need to change out the class. Once I change out the class I can use the code below to hit both up and turn them into drop down menus. Below the text reads Go To... and I need each menu to have something different.

Example here:
Full navigation: http://www.whelandesign.com/nav1.png
Drop down nav: http://www.whelandesign.com/nav2.png

$(function() {
	   
      // Create the dropdown base
      $("<select />").appendTo(".menu");
      
      // Create default option "Go to..."
      $("<option />", {
         "selected": "selected",
         "value"   : "",
         "text"    : "Go to..."
      }).appendTo(".menu select");
      
      // Populate dropdown with menu items
      $(".menu a").each(function() {
       var el = $(this);
       $("<option />", {
           "value"   : el.attr("href"),
           "text"    : el.text()
       }).appendTo(".menu select");
      });
      
	   // To make dropdown actually work
	   // To make more unobtrusive: http://css-tricks.com/4064-unobtrusive-page-changer/
      $(".menu select").change(function() {
        window.location = $(this).find("option:selected").val();
      });
	 

Again, Thanks for your patience and help with this. There is still a lot I have to learn about Drupal.
peace
tim

emilorol’s picture

Hi Tim,

I am sorry for the late reply believe or not I had a baby during that time (honest true), lol.

I was thinking about your issue and I had an idea it might work for you:

1. You are using jQuery to do the change from UL's to SELECT's using the ".appendTo()" method.

2. So a simple solution will be to use the "block id" as part of the selector like .appendTo("#block-block-X .menu") where X is the ID of the block you created to include the menu. Please note the id can be different.

An alternative solution to use the block ID or better yet to have some control over it you can use the "block class" module and that way you can add/name your CSS class you any block on your site in that case the code will be like .appendTo(".YOUR-CUSTOM-CLASS .menu")

Note: remember that regardless of the use of the id or class of the block you have to update your jQuery file.

Emil Orol
"In the computer business you’re either a one or a zero and I am determined never to be zero."

emilorol’s picture

--my bad I posted it twice. this post can be deleted.

Emil Orol
"In the computer business you’re either a one or a zero and I am determined never to be zero."

Jason Dean’s picture

@emilacosta

After struggling for 3 hours to have a different class for the parent UL, I finally found your solution. Simple, elegant... you're a star :)

tchopshop’s picture

Thank you so much for this!

Elena Reeves-Walker
Tchopshop Media
tchopshop.com

pvasili’s picture

/* main ul */
function youtheme_menu_tree__main_menu($variables) {
  return '<ul class="topNav">' . $variables['tree'] . '</ul>';
}

/* inner ui */
function youtheme_menu_tree__main_menu_inner($variables) {
    return '<ul class="innerNav">' . $variables['tree'] . '</ul>';
}

/* inner li */
function yuotheme_menu_link__main_menu_inner($variables) {
  $element = $variables['element'];
  $sub_menu = '';

  if ($element['#below']) {
    $sub_menu = drupal_render($element['#below']);
  }
  $output = l($element['#title'], $element['#href'], $element['#localized_options']);
  return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}

/* main li */
function youtheme_menu_link__main_menu(array $variables) {
  $element = $variables['element'];
  $sub_menu = '';

  if ($element['#below']) {
    foreach ($element['#below'] as $key => $val) {
      if (is_numeric($key)) {             
        $element['#below'][$key]['#theme'] = 'menu_link__main_menu_inner'; // 2 lavel <li>
      }
    }
    $element['#below']['#theme_wrappers'][0] = 'menu_tree__main_menu_inner';  // 2 lavel <ul>
    $sub_menu = drupal_render($element['#below']);
  }
  $output = l($element['#title'], $element['#href'], $element['#localized_options']);
  return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}

sujoyit2005’s picture

The code is very useful.
I have one query: for the 2nd level

    should we add suffix "_inner" at the end of the function name?
    for example the function mytheme_menu_tree__menu_top_menu($variables) becomes mytheme_menu_tree__menu_top_menu_inner($variables)
    and what about
      the is in 3rd level?
sinasalek’s picture

Excellent solution, tx

sina.salek.ws, Software Manager & Lead developer
Feel freedom with open source softwares

AbbeyLashly’s picture

Thank you man. your mama will never die and you. I am too happy!!!

aegirone’s picture

I can't understand , what does "greeceforyou" mean ? thx

punyaruchal’s picture

@emilorol this code work good but i have little problem. i hava a third level menu please see the below image:

Only local images are allowed.

punyaruchal’s picture

Hi Emil Orol this code work good but i have little problem. i have a third level menu. please see the below image:
http://punya.com.np/sites/default/files/blog/pimg.jpg

ja_ca’s picture

I know this is old, but do you remember where you added the unique keyword? Just from your answer I cant understand where exactly to put it.. in the $variables['tree'] array? If yes, where exactly? Thx in advance!

Yuvaraj_G’s picture

what is that mean (KEYWORD-submenu) . could you explain me please....

Christopher James Francis Rodgers’s picture

My CSS3 rough draft notes (partial :o))

CSS 3 - Selectors - Structural - Nth-type pseudo-class.

This CSS3 pseudo-class selector matches elements
on the basis of their positions within a parent element’s list
of child elements.

Accepts an argument, N, which can be:

- keyword
- number
- number expression of the form an+b.

:nth-child(N)
:nth-last-child(N)
:nth-of-type(N)
:nth-last-of-type(N)

Eg. - partial.
:nth-child(even)
:nth-child(odd)

Format.
:nth-child(an + b)
a - Every a-th (optional)
n - Always 'n'?? - Base value.
b - Offset. (optional) Where the count begins.

- Single item. (Eg. The seventh.)
li:nth-child(7) {
color: red;
}

nth-child

(2n)
- Every other (even)
li:nth-child(2n) {
color: red;
} - Starting at zero.
(Zero implied as default.)

(2n+1)
- Every other starting at 1 (odd).
li:nth-child(2n+1) {color: red;
}

(3n+5)
- Start with the 5th item and
every 3rd item there-after.
li:nth-child(3n+5) {color: red;}

(-2n+8)
- Start at eighth, and back
every 2nd.
li:nth-child(-2n+8) {
color: red;
}

(3n-1)
- Starting at -1; every third forward.
li:nth-child(3n-1) {
color: red;
}

(1n+4)
- 4th and each there-after.
li:nth-child(1n+4) {
color: red;
}

nth-last-child.

:nth-last-child(N) matches elements
that are followed by N-1 siblings
in the document tree.

(-n+4)
- Last four items.
li:nth-last-child(-n+4) {
color: red;
} Start from end of list and
work backwards: Starting at +4th back,
and in the (-an) opposite direction;
every nth. (Eg. n = 1n = every-one)

(1n+4)
- All except last 3.
li:nth-last-child(1n+4) {
color: red;
} Start from end of list and
work backwards: Starting at
4th from end, and every 1 in
that backwards direction.

Multiple nth's.

Both ends..
nth-..(-1n+3), nth-last-..(-1n+3)
- First three and last three.
li:nth-child(-1n+3),
li:nth-last-child(-1n+3) {
color: red;
}

Middle ground..
Daisy:Chain:
nth-..(1n+4):nth-last-..(1n+4)
- All except first three and
last three.
li:nth-child(1n+4):nth-last-child(1n+4) {
color: red;
}
...or similarly...
li:nth-last-child(1n+4):nth-child(1n+4) {
color: red;
}


All the best; intended.
-Chris (great-grandpa.com)
___
"The number one stated objective for Drupal is improving usability." ~Dries Buytaert *