function theme_menu_link

You are here

7 menu.inc theme_menu_link(array $variables)
8 menu.inc theme_menu_link(array $variables)

Returns HTML for a menu link and submenu.

Parameters

$variables: An associative array containing:

  • element: Structured array data for a menu link.

Related topics

File

includes/menu.inc, line 1625
API for the Drupal menu system.

Code

function theme_menu_link(array $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";
}

Comments

I needed to style a few random menu links. Main and secondary menus has a great class .menu-123 (.menu-mlid) to do that. So here how you can port the same behavior to any/all menu items:

<?php
/**
* Add unique class (mlid) to all menu items.
*/
function superseven_menu_link(array $variables) {
 
$element = $variables['element'];
 
$sub_menu = '';

 

$element['#attributes']['class'][] = 'menu-' . $element['#original_link']['mlid'];

  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";
}
?>

Or is this the wrong way to do it?

<?php
function superseven_menu_link(array $variables) {
 
$variables['element']['#attributes']['class'][] = 'menu-' . $variables['element']['#original_link']['mlid'];

  return

theme_menu_link($variables);
}
?>

flush caches:

hook_menu_link_alter($variables){
  var_dump($variables);
}

/**
* Add unique ID (mlid) to all menu items.
*/
function THEMENAME_menu_link(array $variables) {

$element = $variables['element'];
$sub_menu = '';

$element['#attributes']['id'][] = 'menu-' . $element['#original_link']['mlid'];

if ($element['#below']) {
$sub_menu = drupal_render($element['#below']);
}

$output = l($element['#title'], $element['#href'], $element['#localized_options']);

return '

  • ' . $output . $sub_menu . "
  • \n";

    }

    <?php
    /**
    * Add unique class (mlid) to all menu items.
    * with Menu title as class
    */

    function yourtheme_menu_link(array $variables) {
     
    $element = $variables['element'];
     
    $sub_menu = '';
     
    $name_id = strtolower(strip_tags($element['#title']));
    // remove colons and anything past colons
     
    if (strpos($name_id, ':')) $name_id = substr ($name_id, 0, strpos($name_id, ':'));
    //Preserve alphanumerics, everything else goes away
     
    $pattern = '/[^a-z]+/ ';
     
    $name_id = preg_replace($pattern, '', $name_id);
     
    $element['#attributes']['class'][] = 'menu-' . $element['#original_link']['mlid'] . ' '.$name_id;
      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";
    }
    ?>

    Hey thanks it works great!

    Do you know where I can get the $element array definition?

    Thanks!

    <?php
    function [your_theme]_menu_link(array $variables) {
     
    $element = $variables['element'];
     
    $sub_menu = '';
     
     
    $element['#attributes']['class'][] = 'level-' . $element['#original_link']['depth'];

      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";
    }
    ?>

    I've spent half a day trying to get the depth when I've stumbled across your example with $element['#original_link']['depth']. Thanks, that solved a huge problem on my part!

    I wanna add class to the whole menu: not to the "il", but to "ul".
    How to do its?

    tazir1: @see attributes at theme_links()

    This gave me some trouble so I thought I'd share.

    We needed to style only a specific menu block (top nav) but not the sidebar nav that was generated off of the same menu.

    So theme_menu_link__main_menu() did not work for us.

    However, I was able to target only the block I needed using the menu block module and theme_menu_link__menu_block__[block-number]()
    described under "Theme Menu functions".

    Hopefully this helps some one else as well.

    Here is how to replace ul class .menu to .sf-menu. for the main menu.

    add this to your template.php
    function YOUR-THEME-NAME_menu_tree__main_menu($variables) {
    return '

      ' . $variables['tree'] . '

    ';
    }

    in bartik page.tpl.php main menu is rendered using theme_links and not theme_menu_link, this is why you need to implement theme_links__system_main_menu

    I found this helpful when customizing the link info.

    <?php
    //print out variables for the link array
    function theme_menu_link(array $variables) {
      return
    print_r($variables['element']);
    }
    ?>

    I don't see it documented elsewhere that you can implement a THEMENAME_menu_link__MENU_NAME() function as an override for a specific menu.

    See http://drupal.org/node/254940#theme-suggestions-for-menus.

    wgsimon, to target specific menu, use the name of the menu from admin/structure/menu page (replace any hyphens with underscores) and append your theme name to the beginning of the function.

    Make any menu specific changes to the body of the function and you are done!

    Once you are ready to view your theme's override function in action, remember to clear your theme registry.
    If you have Devel module installed, turn on "Rebuild the theme registry on every page load" option also helps.

    I can not modify the menu template.
    I clear the cache site and I use devel module and the option "theme registry" without results.

    With print_r I get this:

    [# theme] => Array
    (
    [0] => menu_link__menu_block__5
    [1] => menu_link__menu_block__menu_product_menu
    [2] => menu_link__menu_block
    [3] => menu_link__menu_product_menu
    [4] => menu_link
    )

    In the name of my function, if I use "Mytheme_menu_link__menu_block__5" or "Mytheme_menu_link__menu_block__menu_product_menu" or "Mytheme_menu_link__menu_product_menu" that does not work.
    Only the function name "Mytheme_menu_link" works.

    Do you have an idea?
    Thank you for your help.

    Drupal 7.14

    Does anyone knows how to do this for superfish menu item? It seems the theme_menu_link function is never called.

    For Superfish, please use:

    hook_superfish_menu_item_link(array $variables)

    instead.
    See: https://drupal.org/node/1534734

    As same for @kumkum29 and @richsky
    No override code is working
    themeName_menu_link__main-menu
    or themeName_menu_link__main_menu

    This patch is already there http://drupal.org/node/1001146

    I want to implement it for superfish menu too.

    I found that adding extra html or css to superfish is super easy.

    Basically you edit your Superfish block at [site]/admin/structure/block/manage/superfish/1/configure.

    Then in the Advanced HTML and Advanced CSS sections towards the bottom, you can add HTML before and/or after each menu level, and give specify names in the CSS section. In the HTML section, use the comma to separate the code that goes AFTER the specific level of HTML. It works great!

    I just got to "disable" some menu items' links (while keeping them showing) on my main menu by doing this:

    <?php
    function THEMENAME_menu_link(array $variables) {
     
    $element = $variables['element'];
     
    $sub_menu = '';
     
    // Menu links with id's included in this array will get muted
     
    $muted_mlids = array(577, 578, 579);
      if (
    in_array($element['#original_link']['mlid'], $muted_mlids)) {
       
    // Get it pointing to the current page
       
    $element['#href'] = current_path();
       
    // Ideally should just use an empty fragment, but Drupal ignores it if set like that
       
    $element['#localized_options']['fragment'] = 'none';
      }

      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";
    }
    ?>

    If you need to inject HTML into the title, set the HTML attribute like so for each element that needs parsing.

    $variable['#localized_options']['html'] = 1;

    Here's the code that I used:

    <?php
    /*
    * Implementation of hook_menu_link().
    */
    function THEME_menu_link(array $variables) {
      foreach(
    $variables as &$variable) {
       
    $variable['#title'] = THEME_replace_words($variable['#title']);
       
    $variable['#localized_options']['html'] = 1;
      }

      return

    theme_menu_link($variables);
    }
    ?>

    Amarjit

    Doesn't seem to be working for primary and secondary links under zen subtheme. Or am I missing an obvious different hook I should be using for that?

    Hi,
    It is working for me if I follow these steps (maybe could be done in a simplest way, but I don't know how).

    1. Copy the page.tpl.php from zen/templates/page.tpl.php
      to
      themes/my_zen_subtheme/templates/page.tpl.php
    2. Add this code to your my_theme_subtheme/template.php file
      function my_zen_subtheme_links__system_main_menu(array $variables) {
        // We can debug a little using kpr (similar to dpm)
        kpr($variables);
        // Changing title menu for one link (hardcoded, you should add logic to get the menu element you want)
        $variables['links']['menu-1920']['title'] = "My new title";
        return theme_links($variables);
      }

    It is useful to read the page.tpl.php line 141.

    // This code snippet is hard to modify. We recommend turning off the
    // "Main menu" on your sub-theme's settings form, deleting this PHP
    // code block, and, instead, using the "Menu block" module.
    // @see http://drupal.org/project/menu_block
    print theme('links__system_main_menu', array(

    I'm new to Drupal and find it profoundly frustrating that the input parameter for this function is glossed over as "structured array data for a menu link" as if it is not relevant to the reason one might seek this documentation. Here is what I found $variables['element'] to consist of (in this case, just an external link to facebook):

    Array
    (
        [element] => Array
            (
                [#theme] => menu_link__social_chicklets
                [#attributes] => Array
                    (
                        [class] => Array
                            (
                                [0] => first
                                [1] => leaf
                            )

                    )

                [#title] => facebook
                [#href] => https://www.facebook.com/
                [#localized_options] => Array
                    (
                    )

                [#below] => Array
                    (
                    )

                [#original_link] => Array
                    (
                        [menu_name] => social_chicklets
                        [mlid] => 397
                        [plid] => 0
                        [link_path] => https://www.facebook.com/RogerCPAReview
                        [router_path] =>
                        [link_title] => facebook
                        [options] => Array
                            (
                            )

                        [module] => menu
                        [hidden] => 0
                        [external] => 1
                        [has_children] => 0
                        [expanded] => 0
                        [weight] => 0
                        [depth] => 1
                        [customized] => 0
                        [p1] => 397
                        [p2] => 0
                        [p3] => 0
                        [p4] => 0
                        [p5] => 0
                        [p6] => 0
                        [p7] => 0
                        [p8] => 0
                        [p9] => 0
                        [updated] => 0
                        [load_functions] =>
                        [to_arg_functions] =>
                        [access_callback] =>
                        [access_arguments] =>
                        [page_callback] =>
                        [page_arguments] =>
                        [delivery_callback] =>
                        [tab_parent] =>
                        [tab_root] =>
                        [title] => facebook
                        [title_callback] =>
                        [title_arguments] =>
                        [theme_callback] =>
                        [theme_arguments] =>
                        [type] =>
                        [description] =>
                        [in_active_trail] =>
                        [access] => 1
                        [href] => https://www.facebook.com/
                        [localized_options] => Array
                            (
                            )

                    )

                [#children] =>
            )

        [theme_hook_suggestions] => Array
            (
            )

    )

    Here's the approach I took to get the right markup for the bootstrap dropdown menu. It's a bit hacky but it might point someone in the right direction.

    In `template.php`:

    <?php
     
    function THEMENAME_preprocess_page(&$vars) {
       
    // ....
     
        // Get the entire main menu tree
       
    $main_menu_tree = menu_tree_all_data('main-menu');

       

    // Add the rendered output to the $main_menu_expanded variable
       
    $vars['main_menu_expanded'] = menu_tree_output($main_menu_tree);
      }
     
      function
    THEMENAME_links__system_main_menu($vars) {
       
    $class = implode($vars['attributes']['class'], ' ');
       
    $html = '<ul class="' . $class . '">';
        foreach (
    $vars['links'] as $key => $link) {
          if (
    is_numeric($key)) {
           
    $sub_menu = '';
           
    $link_class = '';
           
    $link_title = $link['#title'];
           
    // Check for sub menu - note I've only checked this for 2 levels
            // it might not work for 3 level menus.
           
    if (!empty($link['#below'])) {
             
    $link_class = ' class="dropdown"';
             
    $link['#attributes']['class'][] = 'dropdown-toggle';
             
    $link['#attributes']['data-toggle'][] = 'dropdown';
             
    // And recurse.
             
    $sub_menu = theme('links__system_main_menu', array('links' => $link['#below'], 'attributes' => array('class' => array('dropdown-menu'))));
             
    // Add drop down caret.
             
    $link_title .= '<b class="caret"></b>';
            }
           
    $html .= '<li' . $link_class . '>' . l($link_title, $link['#href'], array('html' => 'true', 'attributes' => $link['#attributes'])) . $sub_menu . '</li>';
          }
        }
       
    $html .= '</ul>';

        return

    $html;
      }
    ?>

    And in page.tpl.php print the menu:

    <?php
     
    <?php print theme('links__system_main_menu', array(
       
    'links' => $main_menu_expanded,
       
    'attributes' => array(
         
    'id' => 'main-menu',
         
    'class' => array('nav', 'clearfix'),
        ),
       
    'heading' => array(
         
    'text' => t('Main menu'),
         
    'level' => 'h2',
         
    'class' => array('element-invisible'),
        ),
      ));
    ?>

    ?>

    Above is D7

    Thanks for share your code, main menu dropdown now working when clicked the carret.

    I have a situation where I have replaced the "Log out" text in the user menu with an image. This works fine on some pages (custom pages and views), but when I am on a node (/node/65 for example) then the image is not displayed and I get a log entry of type "page not found" with message "node/sites/all/themes/mytheme/images/sign.out.jpg". I am using omega and have replaced the image with the following in template.php. Any thoughts on why this is happening?

    ----------------------------------------------------------------------------------
    function mytheme_menu_link__user_menu($variables){
    $element = $variables['element'];
    $sub_menu = '';

    if ($element['#below']) {
    $sub_menu = drupal_render($element['#below']);
    }

    $title = $element['#title'];
    $link = $element['#href'];
    $options = $element['#localized_options'];

    $output = "";
    if( $element['#title'] == 'My account' ){
    $user = $GLOBALS['user'];
    $title = "Welcome ".$user->name."!";
    unset($options);
    $options = array();
    $options['attributes'] = array('class' => 'foo_user_menu');
    $output = l($title, $link, $options);
    }elseif( $element['#title'] == 'Log out' ){
    $title = "<img src=\"sites/all/themes/mytheme/images/sign.out.jpg\" />";
    unset($options);
    $options=array();
    $options['html'] = TRUE;
    $output = l($title, $link, $options);
    }else{
    $title = $element['#title'];
    $output = l($title, $link, $options);
    }

    return $output;
    }

    The line: $title = "";
    needed to be: $title = "";

    The line: $title = "<img src=\"sites/all/themes/mytheme/images/sign.out.jpg\" />";
    needed to be: $title = "<img src=\"/sites/all/themes/mytheme/images/sign.out.jpg\" />";

    So /sites and not sites.

    first off thanks loominade for the example to get me started, I expanded on it to add the level class to the ul and a tags, to return output:

    ul class="menu level-1">
    <li class="expanded active-trail level-1">
    <a class="active-trail level-1" href="/test-page-1">Test Page 1</a>
    <ul class="menu level-2">
    <li class="first last expanded active-trail level-2">
    <a class="active-trail level-2" href="/node/2">Test Page 2</a>
    and so on...

    this is the code that goes in template.php, its made to work w/ main menu, but you can alter it to work w/ any menu just change occurrences of main_menu (there are 2) w/ the machine name of your menu.

    <?php
    function <your_theme_name>_menu_link(array $variables) {
     
      if (
    $variables['element']['#theme']== 'menu_link__main_menu') {
     
       
    $element = $variables['element'];
       
    $sub_menu = '';
       
       
    $level = 'level-' . $element['#original_link']['depth'];
       
    $element['#attributes']['class'][] = $level;
       
    $element['#localized_options']['attributes']['class'][] = $level;
       
        if (
    $element['#below']) {
         
    $sub_menu = drupal_render($element['#below']);
        }
       
    $output = l($element['#title'], $element['#href'], $element['#localized_options'] );
        return
    '<ul class="menu ' . $level .'"><li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li></ul>\n";

      }
     
    }
     
    function <

    your_theme_name>_menu_tree__main_menu($variables) {
     
    //this removes the original ul tag with class="menu"
     
    return $variables['tree'];
    }
    ?>

    on further testing the above code still needs some work to properly add level class to

      element, I will post new code when fixed.

    this code works, it adds the the class - to all ul li and a tags.
    I got the code from here: https://gist.github.com/henrijs/6225177 and added the class to the a tag

    <?php
    function themename_menu_link(&$variables) {
     
    $element = $variables['element'];
     
    $sub_menu = '';

     

    $element['#attributes']['data-menu-parent'] = $element['#original_link']['menu_name'] . '-' . $element['#original_link']['depth'];
     
    $element['#localized_options']['attributes']['class'][] = $element['#original_link']['menu_name'] . '-' . $element['#original_link']['depth'];

      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";
    }
    ?>
    <?php
    function themename_preprocess_menu_tree(&$variables) {
     
    $tree = new DOMDocument();
      @
    $tree->loadHTML($variables['tree']);
     
    $links = $tree->getElementsByTagname('li');
     
    $parent = '';

      foreach (

    $links as $link) {
       
    $parent = $link->getAttribute('data-menu-parent');
        break;
      }

     

    $variables['menu_parent'] = $parent;
    }
    ?>
    <?php
    function themename_menu_tree(&$variables) {
      return
    '<ul class="menu ' . $variables['menu_parent'] . '">' . $variables['tree'] . '</ul>';
    }
    ?>

    I modify one Menu item with menu_link , this works fine but all the children of this item are not expanded anymore, how do I achieve this? If I do not modify the menu item the menus is expanded correctly

    Code:
    // modify Standort menus
    function Lernpraxis_menu_link(array $variables) {

    //mlid - menu link ID. I want AJAX only some menu link
    if ($variables['element']['#original_link']['mlid'] == 865) {
    drupal_add_library('system', 'drupal.ajax');
    $element = $variables['element'];
    $output = l('Lernpraxis am Klus Park', 'team', array('attributes' => array('data-option-value' => array('.lernpraxis-am-klus-park')),'fragment' => 'filter=.lernpraxis-am-klus-park', 'expanded' => TRUE));
    return '

  • ' . $output . "
  • \n";
    }

    // render other items dy default way
    return theme_menu_link($variables);
    }