16

I've cruised around the web and it is not at all obvious how to theme custom menus. I've looked for hours and have not found one single post that illustrates the process, from start to finish, of creating a menu and customizing its output. It seems like this is a multi-stage process:

  1. Create the menu through the Drupal interface.
  2. Create a theme function in your template.php file to theme the output.
  3. Expose this menu to a template file (somehow) by adding it as a variable.
  4. Call the theme function on the menu in the template file.

1 is easy enough to do, the problems I run into are with 2, 3, and 4. Looking at the default page template, I see that it exposes the main menu in the variable $main_menu. Later down the page you can see the function theme('links__system_main_menu', array('links' => $main_menu..., which means it's looking for an appropriately named theme function somewhere and using it to generate the output.

I know if I place function theme_links__system_main_menu(&$variables) {...} in my template.php file Drupal will use that function as opposed to function theme_menu_links(&$variables) {...}.

What I don't know is how Drupal links the custom menu I created with that function. Let's say for example that I created a menu called My Menu. Could I create the following function in my template.php file and theme the output for that menu? function theme_links__system_my_menu(&$variables) {...}

Also, how does one make that custom menu available to a template file? How does Drupal expose the $main_menu variable to page.tpl.php?

I think the key I'm missing here is how to embed the variable that represents my custom menu into a template page. For the most part though I'm completely lost with what to do after having created the menu.

Thanks for the help.

EDIT: Probably should post what I'm doing. Per my comment to BetaRide below, I need to inject customized HTML into the <li> elements of the menu items. Specifically, I'm adding Twitter Bootstrap icons.

4 Answers 4

13

Drupal's implementation of menus is a bit special, It doesn't always work the way it seems it should.

You can take a look at the core implementation of template_preprocess_page() to how the Main Menu links are added as a variable to the page template. You have to drill down a bit in the API docs, but the function you want to call in your implementation of theme_preprocess_page() is menu_navigation_links(), which will return an array of links in the menu.

Looking at line 106 of Drupal core's page.tpl.php file, you can see how the main menu links are themed in the template by calling the theme() function with a hook of 'links__system_main_menu'.

Theoretically, this implementation should be able to be duplicated with a custom menu by following the standard naming conventions. So, in template.php, you could have:

function THEMENAME_preprocess_page(&$variables){
  $variables['custom_menu'] = menu_navigation_links('menu-custom-menu');
}

function THEMENAME_links__menu_custom_menu(&$variables){
 //custom theme function here
}

and in page.tpl.php, you would add something like this:

<?php print theme('links__menu_custom_menu', array('links' => $custom_menu, 'attributes' => array('id' => 'custom-menu', 'class' => array('links', 'inline', 'clearfix')), 'heading' => t('Custom menu'))); ?>

However, adding the custom menu as a variable on your page template is not necessary. You could easily place the custom menu's block into a desired region via the Drupal admin interface. Also, you could change the site's settings for the Main Menu source, effectively replacing the default $main_menu variable in page.tpl.php with your custom menu.

EDIT: I am just seeing your addition about your end goal being to just add some custom html to the menu items for icons. Depending on how you are adding these icons, there are a couple different Drupal module options.

Menu Icons - allows you to upload an image via the Menu item's settings and automatically generates CSS (customizable via a template) that adds the image as a background on the menu item.

Menu Attributes - allows you to add a custom class to each menu item via its settings in the admin. Once a unique class is added to each menu item, you could use CSS to add an icon to the menu item or use javascript to inject the additional HTML into the menu item.

3
  • This seems to answer all of my questions perfectly. This is a great answer. I'll try this out immediately. Mar 7, 2012 at 17:12
  • 1
    It worked, but I took everyone's unanimous opinion to heart and I didn't use this method. I used the Menu Attributes module that you suggested and assigned unique IDs to the specific menu items, and then used jQuery to inject the HTML directly where I needed it to be. Thanks for the tips. +1 Mar 10, 2012 at 2:31
  • "You could easily place the custom menu's block into a desired region via the Drupal admin interface" ++ . I find this solution to be way simpler.
    – cdmo
    Mar 19, 2015 at 15:29
1

You may want to look into the Nice Menus module. Here is a quote about it (from the module's project page):

... enables drop-down/right/left expandable menus. It uses only CSS for most browsers, with minimal Javascript for IE6. (Version 2 uses the Superfish jQuery plugin for all browsers, with an option to disable JS, and falls back to CSS-only for browsers that can handle it.)

Three styles/types of menus are currently possible: horizontal, menus drop down; vertical, menus fly to the left; vertical, menus fly to the right. There is a handbook page that provides a list of sites that use Nice menus.

Nice Menus creates blocks that may be associated with any existing site menu which can be placed wherever normal blocks can be placed in a theme. For themers, it is also possible to theme a menu as a Nice Menu directly by using the provided theme functions so a block is not necessary. A specific theme function for the Primary Links menu is available. The theme functions also allow a developer to pass in a custom menu tree of their making (i.e. not using a Drupal menu.) There is more information on how to use theme functions in the documentation.

The module comes with a simple, generic color scheme which can be fully overridden either by adding the override CSS to the theme's normal stylesheet or by creating a Nice Menus CSS file and telling Nice Menus to use that rather than its own default one through the global theme configuration. Several CSS override examples are provided in the included README.txt file and in the handbook.

3
  • I don't see why somebody voted this answer down. With Nice Menus, you can target an individual menu item since it has its own class. Then you can use a graphic as the background for that individual class. I have done it before on a couple of major projects. Mar 7, 2012 at 16:01
  • 2
    I didn't vote it down, but I will say that this does not address the heart of the question(s) I asked, which is how to dig into the core of Drupal theming for menu items. This is why these questions never get answered fully, because there is always some module that someone name drops. I don't care about modules, I want to do template coding. Mar 7, 2012 at 16:05
  • 1
    I didn't down vote either, but want to add to what Lester said. When you suggest a module in your answer you should also explain why you are suggesting it and how it can be used to solve the OP's issue. Without those details, your answer can be seen as not very helpful and potentially be down voted. I made that mistake on my first few answers here, too.
    – sheena_d
    Mar 8, 2012 at 0:08
0

Depending on what you want to customize, your approach is much to havy. Usually I create the menu through the Drupal interface as you sugested. The I take the theme developer module and Firebug to find out whicht templates, hooks and CSS directives I have to overwrite to tweak it to my needs.

It's worth considering creating a sub theme of the base theme you are using befor you start tweaking. This makes it much easier to update your base theme.

1
  • I need to inject customized HTML into the <li> elements of the menu items. Specifically, I'm adding Twitter Bootstrap icons. Mar 7, 2012 at 14:22
0

here is a smart code that can access to all menu items for any level 2 or 3 or more

place this code inside your tpl file don't forget to call boostrap js and css last versions :

<!-- menu -->  

        <?php 

          # get menu with all levels 
          menu_tree_all_data('main-menu');
          $menu = menu_build_tree('main-menu');


          # help function for listing submenus for each link     
          function sub_menu_links($var){
           $submenu ='<ul class="dropdown-menu">';

              foreach ($var as $sub) {

                $path = str_replace("<front>",'' ,$sub["link"]["link_path"]);

                 if(count($sub["below"]) > 0 ){
                      $submenu .= '<li class="dropdown-submenu" ><a href="javascript:void(0);" >'.$sub["link"]["link_title"].'</a>';
                      $submenu .=  sub_menu_links($sub["below"]);
                      $submenu .= '</li>'; 
                  }else{
                      $submenu .= '<li><a href="'.$path.'">'.$sub["link"]["link_title"].'</a>'; 
                      $submenu .= '</li>'; 
                   }

              }
            $submenu .= ' </ul>';

            return $submenu;

          }


          # help function for more than 2 levels
          function menu_links($menu){

             $links = '<ul class="nav navbar-nav">';
             foreach ($menu as $link) {

              if(count($link["below"]) > 0 ){ 

                $path = str_replace("<front>",'' ,$link["link"]["link_path"]);

                $links .=  '<li class="dropdown ">';
                $links .= '<a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown">'.$link["link"]["link_title"].'</a>';
                      /* print "<pre>";
                      var_dump( $link["below"]);
                       print "</pre>";*/
                $links .= sub_menu_links($link["below"]); 


                $links .= '</li>' ;

              }else{
                  $links .= '<li>';
                  $links .= '<a href="'.$path.'">'.$link["link"]["link_title"].'</a>'; 
                  $links .= '</li>' ;
              }



              } 
              $links .= '</ul>';
              return  $links ;
          } 


          print menu_links($menu);


           ?>

this code will return the menu for css classes you can use your own i'm using bootstrap with some custom css

this code is after a hard work it can save your time and it's tested on drupal 7.x and works fine for main-menu you can change the menu as you like

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.