Drupal's menu system lets you define an arbitrary number of menu items in a hierarchy, each of which is a link to somewhere in the site (or on another site). That's great, unless you want to have a menu tree that has items in it which are not, in fact, links. Several scenarios for this exist, including parent categories and section separators.

This page explains how to add "menu links" that will not actually link anywhere.

Modules

Modules that allow links that are not links:

Theme layer: Drupal 5

Alternatively, this can be accomplished in your theme:

Step 1

We will define the magic value <none> to mean "a path to nowhere", just like <front> means "the home page". The crucial part here is the theme_menu_item_link() function, which we will override. Place the following function in your template.php file.

function phptemplate_menu_item_link($item, $link_item) {
  if ($item['path'] == '<none>') {
    $attributes['title'] = $link['description'];
    return '<span'. drupal_attributes($attributes) .'>'. $item['title'] .'</span>';
  }
  else {
    return l($item['title'], $link_item['path'], !empty($item['description']) ? array('title' => $item['description']) : array(), isset($item['query']) ? $item['query'] : NULL);
  }
}

Any menu item whose path is <none> will now become a span rather than a link. Note that because it's not a link you can't click on it, so you can't access sub-menu items unless the no-link menu item has the "Expand" checkbox checked so that its children are always visible. If you need to have access to the sub-menu items for things like dropdown/flyout menus, try using the JavaScript version of this snippet.

This method can also be used as a separator. Simply have a menu item with text "------" or similar, and set its path to <none>. It will then show as text with no link, providing a nice separation of the menu.

Step 2

As a nice extra, we can also add additional help text to the menu item edit page. For that you will need to implement a very small module that implements form_alter. The module itself doesn't need anything more than the following code snippet:

function example_form_alter($form_id, &$form) {
  if ('menu_edit_item_form' == $form_id) {
    $form['path']['#description'] .= ' ' . t('Enter %none to have a menu item that generates no link.', array('%none' => '<none>'));
  }
}

That will modify just the menu item edit form and add additional text to the description (the text that appears below the textfield) describing how to use the <none>.

Drupal 6

In Drupal 6 (and 7) you cannot use the value <none> as the target path. You need to define a valid target first.

Add a node with the path alias "nolink" as a dummy to identify this link isn't a link.

Add this to template.php:

function YOURTHEMENAME_menu_item_link($link) {
  if ($link['type'] && $link['href'] == 'nolink') {
    return '<span class="nolink">'.check_plain($link['title']).'</span>';
  }
}

Now add a menu item with "nolink" as target path. Style the .nolink class to your needs

Drupal 7

For Drupal 7 follow the Drupal 6 approach and use this function in template.php:

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

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

Drupal 8

Related issue for 8.x core:
#1543750: Allow menu items without path

Quick solution

Put the below line in path & put your desire title
http://#

Use the below CSS to avoid linking. You must add the "nolink" class, which can be done with the code above, or using a module like https://www.drupal.org/project/menu_attributes

.nolink {
   pointer-events: none;
   cursor: default;
}

Comments

tekket’s picture

This is the only version that works for me with Nice Menus module... (I also had strange problems when I tried the dummy node approach, so I used the fake url one - in this case I use http://fake.link for every item I do not want to be linkable)

function themename_menu_item_link($link) {
  if (empty($link['localized_options'])) {
    $link['localized_options'] = array();
  }

  if (!$link['page_callback'] && strpos( $link['href'], 'fake.link')) {
    return '<a href="#" class="nolink">'. $link['title'] .'</a>';
  }
  else {
        return l($link['title'], $link['href'], $link['localized_options']);
  }
}

Note: class nolink is purely optional, but it's good for adding some useful styling like changing the cursor etc.

pjabode’s picture

This version worked for me (Great work!)

to avoid having the page scroll to the top when clicking on the fake link,
use
javascript:void(0)

instead of the

#
sign
inside of return '

digitallica’s picture

This solution works for nice menus (and other menu's occasions where dummy link needed [?])

A quick additional improvement: insert

 onclick="return false"

to the return code for dummy link to become as follows:

<?php
function themename_menu_item_link($link) {
  if (empty($link['localized_options'])) {
    $link['localized_options'] = array();
  }

  if (!$link['page_callback'] && strpos( $link['href'], 'fake.link')) {
    return '<a href="#" onclick="return false" class="nolink">'. $link['title'] .'</a>';
  }
  else {
        return l($link['title'], $link['href'], $link['localized_options']);
  }
}
?>

so dummy link (#) not causing web browser to search whole page for link anchor #

segx’s picture

Thanks digitallica - there was a couple things I wanted to add/share to this improvement. When using nice menus, "menu-path-fake.link" is created as a class in each parent li. I don't believe having a period in the class is good form. I also wanted to utilize javascript:void(0) instead of # so that the current URL isn't read into the href (removing an incorrect URL may be better for SEO?), so I made the below modifications and added a new function from the nice menus module to my sub-theme's template.php file.

<?php
/**
 * Creates empty menu item when http://menu.nolink is added to the menu path.
 */
function THEMENAME_menu_item_link($link) {
  if (empty($link['localized_options'])) {
    $link['localized_options'] = array();
  }

  if (!$link['page_callback'] && strpos( $link['href'], 'menu.nolink')) {
    return '<a href="javascript:void(0)" onclick="return false" class="nolink">'. $link['title'] .'</a>';
  }
  else {
        return l($link['title'], $link['href'], $link['localized_options']);
  }
}

/**
 * Helper function that builds the nested lists of a nice menu.
 *
 * @param $menu
 *   Menu array from which to build the nested lists.
 */
function THEMENAME_nice_menu_build($menu) {
  $output = '';

  foreach ($menu as $menu_item) {
    $mlid = $menu_item['link']['mlid'];
    // Check to see if it is a visible menu item.
    if ($menu_item['link']['hidden'] == 0) {
      // Build class name based on menu path
      // e.g. to give each menu item individual style.
      // Strip funny symbols.
      $clean_path = str_replace(array('http://', '<', '>', '&', '=', '?', ':', 'menu.'), '', $menu_item['link']['href']);
      // Convert slashes to dashes.
      $clean_path = str_replace('/', '-', $clean_path);
      $path_class = 'menu-path-'. $clean_path;
      // If it has children build a nice little tree under it.
      if ((!empty($menu_item['link']['has_children'])) && (!empty($menu_item['below']))) {
        // Keep passing children into the function 'til we get them all.
        $children = theme('nice_menu_build', $menu_item['below']);
        // Set the class to parent only of children are displayed.
        $parent_class = $children ? 'menuparent ' : '';
        $output .= '<li id="menu-'. $mlid .'" class="'. $parent_class . $path_class .'">'. theme('menu_item_link', $menu_item['link']);
        // Build the child UL only if children are displayed for the user.
        if ($children) {
          $output .= '<ul>';
          $output .= $children;
          $output .= "</ul>\n";
        }
        $output .= "</li>\n";
      }
      else {
        $output .= '<li id="menu-'. $mlid .'" class="'. $path_class .'">'. theme('menu_item_link', $menu_item['link']) .'</li>'."\n";
      }
    }
  }
  return $output;
}
?>

Obviously THEMENAME would be the name of your theme. The second function will remove "http://menu." creating the class "menu-path-nolink".

abe’s picture

I installed nice menus and added the code in bartik's template.php
- I then changed the themename to bartik
- cleared all cache (drush cc)
- but I still could not get the menu.nolink url to change to javascript:void(0) after menu was re-displayed.

I also tried the other sample code, and it did not work for me too.

I must be missing some change that must be done. Help.

Drupal's menu system was saving the link as system paths and not url alias. I changed strpos() search string and it worked.

There's a bit of a wrinkle, the parent menu link which is changed with javascript:void(0) may need to be set to 'expanded' and not collapsed.

Thanks.

ShaunDychko’s picture

To help other forum wanderers, I'd like to say that the code provided by tekket works great with Nice Menus. Steps for newbies:
1) Copy and paste the code at the very bottom of your theme's template.php file, but remove the <?php and ?> php tags.
2) notice the function name is themename_menu_item_link... You must replace "themename" with the name of your theme. It could look something like garland_menu_item_link($link) after you're finished.
3) Purge the Drupal cache using the button at the bottom of the /admin/settings/performance page.
4) Enter "http://fake.link" as the link destination for menu items that shouldn't be links.

jmerandy’s picture

tekket's php snippet combo attack with Bassplaya CSS code

yanivfel’s picture

This solution worked for me.
It's a workaround, but it sure save lots of time and hassle, therefore it's a winning one!

BassPlaya’s picture

I know it's not the best but in the worst case scenario:
you could use css by tricking the user to feel that certain links aren't links..
.certain-links a,
.certain-links a:hover {
cursor: default;
text-decoration: none;
}

Authentically,
BassPlaya

jefkin’s picture

My client discovered this 'bug' and demanded a fix, I found this thread and spent 45 minute reading and digesting this issue. Every solution presented seems to ignore the power of Drupals Behaviors.

Obviously my solution works for me, but it could work for you too. It's a matter of your comfort level with Drupal Behaviors and Themes.

1) find the js included in your theme

For me:

sites/themes/all/<my_theme>/scripts.js

Yours will possibly be parallel, with your theme insted of "<my_theme>"

2) Using some Dom inspector, find the tightest css rule you need to isolate your troublesome 'A' fathers:

For me:

#headerblocks ul.nice-menu.nice-menu-down > li > a

3) Add a behavior at the end of the javascript file:

Drupal.behaviors.theme_<my_theme> = function ()
{
  $('#headerblocks ' +
    'ul.nice-menu.nice-menu-down > ' +
    'li > ' +
    'a:not(.<my_theme>-processed)').attr('href', '#')
                                   .addClass('<my_theme>-processed');
};

As is, this snipit won't work, replace <my_theme> with the name of your theme. Also note that the css selector from step 2 is incorporated in the javascript string there, but we add the text [':not(.<my_theme>-process)'] after the 'a'.

The Javascript here is selecting the father 'A's that aren't yet processed and replacing their href with '#' and marking them as processed.

Voila, quick, correct, and clear.

Some possible problem you might have:

  • You're not using a theme with javascript
  • (my suggestion is to change to one, or modify your theme to do so)

  • You can't find the css selector for your Father 'A'
  • (ummm, I can't give any direct advice on this, ask someone about your site)

  • You want to make the cursor style to change blah blah css stuff
  • (Well the last line of code there is ".addClass('someclass');" add your own and use css for styling

  • You want a leaf node with no link, or you want some other combination of links and not links
  • (If you want other hard to define, or specific items, I think you can use secondary rules in your javascript Behavior to specify them, Since these aren't really likely to be needed on every site, consider this a per-site customization, that way you really give the client what they want.)

  • Finally, note that some clients fear Javascript, if this is true of your clients, then they may still have sucky menus 0.o

Have Fun and Good Luck

Jeff

rombapa’s picture

This worked for me. Thanks.

Note that for Drupal 7, you'll need to declare the .js file in your theme .info file, e.g.:

scripts[] = js/script.js

.. and wrap the code in your .js file like this (CSS selectors adapted for my needs):

(function($){ 
	Drupal.behaviors.theme_<my_theme> = {
		attach: function () {
			$('#nice-menu-1 ' +
			'li.menuparent > ' +
			'a:not(.<my_theme>-processed)').attr('href', '#')
	                                   .addClass('<my_theme>-processed');
		}
	};
})(jQuery);
liezie_D’s picture

Thank you, this worked for me.

niranjan81’s picture

just a simple addin to the "nolink" method: If its ssl server, even if you add the function accurately, you will get no effect when "nolink" is set as path verbatim (of course without the double-quotes). the "nolink" is refuted as any other incorrect path. I kept on searching for all possible reasons as to "why the %$#&" is it not working ? :-P
ok, please set the path to "https://nolink" and Volla! :D

Vemma-1’s picture

This is Amazing/ Stupid on how Drupal 7 doesn't have a simple feature for something so basic. I hate downloading mods for something so simple.

guyiac’s picture

For a CMS that does so many amazing things, not having a simple solution for this is absurd. And Drupal folks wonder why Wordpress has become so popular.

IamOnStage’s picture

The Drupal 7 approach is not working?

I have the code:

function bootstrap_business_menu_link(array $variables) {
  $element = $variables['element'];
  $sub_menu = '';
  if ($element['#below']) {
    $sub_menu = drupal_render($element['#below']);
  }
  if (strpos( $element['#href'], 'nolink')) {
    $output = '<a href="#" class="nolink">' . $element['#title'] . '</a>';
  } else {
  $output = l($element['#title'], $element['#href'], $element['#localized_options']);
  }
  return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}

And it is not working?

SocialNicheGuru’s picture

The code for Drupal 7 is not working for me either. too bad

http://SocialNicheGuru.com
Delivering inSITE(TM), we empower you to deliver the right product and the right message to the right NICHE at the right time across all product, marketing, and sales channels.

AndyD328’s picture

Drupal 7 uses the node/n path for menu links, and not the alias that's being tested for. The below should get you there - this adds a url() call to the strpos check to convert the node/n path to /nolink.

function THEME_menu_link($vars) {
  $element = $vars['element'];
  $sub_menu = '';
  if ($element['#below']) {
    $sub_menu = drupal_render($element['#below']);
  }
  if (strpos(url($element['#href']), 'nolink')) {
    $output = '<a href="#" class="nolink">' . $element['#title'] . '</a>';
  } else {
  $output = l($element['#title'], $element['#href'], $element['#localized_options']);
  }
  return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}
E Johnson’s picture

^ Sorry, but that solution isn't working in D7.

Any ideas how to get this to work?

AndyD328’s picture

What path is your relevant menu item taking through the code? And what is the value of $element['#href'] for the item that's not working?

E Johnson’s picture

d-nz,

I copied your function into my template.php file and changed the function name "THEME_menu_link" to my theme name "aplvncc_menu_link". Cleared cache through Performance > Cache, as well as, Devel has "Rebuild the theme registry on every page load" turned on. Am I missing a step?

When I try entering for the menu item path either as "<nolink>" or "nolink", it doesn't get pass the check (ie. The path '<nolink>' is either invalid or you do not have access to it.).

What path is your relevant menu item taking through the code?

Not quite sure what you mean. I don't have any menu modules (Admin Menu/Superfish) installed that would be affecting core to my knowledge.

And what is the value of $element['#href'] for the item that's not working?

aplvncc_menu_link() doesn't appear to be called ever. If I put a print into your function just below:

$element = $vars['element'];
print "|".$element['#href']."|";

Nothing shows up when I view source after I tried to save the menu item.

Thanks for the help.

Running:
- Drupal core 7.28

EDIT: I just achieved the <nolink> with the Special Menu Item module. The Instructions at the top imply you can use either 1 of 3 of those modules OR apply the YOURTHEMENAME_menu_link function to your template file if you don't want to bloat your website with modules. At least, that's how I interpreted it. The latter doesn't work unless you install Special Menu Item for me.

AndyD328’s picture

You don't need the special menu item module to use the code, that's an alternative - not one I've ever used so I can't advise you on it.

Did you 'Add a node with the path alias "nolink" as a dummy to identify this link isn't a link.' ?

If you've done that then the only issue left is that the function isn't being called. Double/triple/quadruple check the name and clear the cache again. If that doesn't help then you're going to have to track down why it isn't being called, it won't work until it is.

Cheers

Anonymous’s picture

Quick solution

Put the below line in path & put your desire title
http://#

When using this quick solution in D7 I noticed that the URL changes literally links to http:///# (3 backslashes). Instead of http://example.com/current-page# what you probably expect it to do.

I'm sure there are people like me searching for a solution to make it work like output in the second example marked, to avoid the output in the first marked example. This easily can be done by removing the backslashes (it's some kind of hack/bug). So when you enter something similar in the path field like this http:# or this http:#anchorname, than the path will be validated as well. Unfortunately the output doesn't differ much in comparison with "#", so yea..

cdracars’s picture

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

if ($element['#below']) {
$sub_menu = drupal_render($element['#below']);
}
if ($element['#href'] == 'http://nolink') {
$output = ''. $element['#title'] . '';
} else {
$output = l($element['#title'], $element['#href'], $element['#localized_options']);
}
return '

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

    Just change the first output to whatever you want and your golden!

    clemence.izm’s picture

    hello,

    the topic is quite old now, but i'm new in drupal and a little lost. I don't find the function code that works for me and wonder is yours doesn't works because there some customization of the code to be made?

    The comment you left at the end of the code make me wonder:
    Just change the first output to whatever you want and your golden

    To what should I change the first output.

    Many thank's in advance.

    Clémence

    renu_mughal’s picture

    Excellent

    fabiogameleira’s picture

    Excellent

    Naveen_K’s picture

    Drupal 7

    This is a solution that worked for me.

    function YOURTHEME_menu_link(array $variables) {
        $element = $variables['element'];
        $sub_menu = '';
    
        if ($element['#below']) {
            $sub_menu = drupal_render($element['#below']);
        }
        if ($element['#href'] == t('http://#')) {
            $output = '<a href="#" class="nolink" clickable="false">' . $element['#title'] . '</a>';
        } else {
            $output = l($element['#title'], $element['#href'], $element['#localized_options']);
        }
        return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
    }
    
    Yuvaraj_G’s picture

    i just want to add this

    <i class="fa fa-plus dropdown-toggle" data-toggle="dropdown"aria-hidden="true"></i>
                              <i class="fa fa-minus dropdown-toggle" data-toggle="dropdown"aria-hidden="true"></i>

    after all parent menu item if it has the child. can any one help me plz..

    mcmacerson’s picture

    Thanks fleuv!

    http:# or this http:#anchorname

    worked for me in Drupal 7!

    ddease2’s picture

    When setting up the menu link, I put the path as http:/# (not http://#). This produced an actual path of http://www.example.com/# for the link, which is also unclickable.

    Thanks!

    xtian7’s picture

    Drupal 7 version, worked fine, I've write this code for make a one page theme:

    function themename_menu_link(array $variables) {
      /*** Get each of the menu elements, one by one ***/
      $element = $variables['element'];
      /*** Variable for store sub-menu (if any) ***/
      $sub_menu = '';
    
      /*** Get the sub-menu (if any) ***/
      if ($element['#below']) {
        $sub_menu = drupal_render($element['#below']);
      }
    
      /*** Alter menu parents, for scrolling ***/
      if (strpos(url($element['#href']), '#')) {
        $link = substr(url($element['#href']), 7);
        $output = '<a href="'.$link.'" class="page-scroll">' . $element['#title'] . '</a>';
      } else {
        $output = l($element['#title'], $element['#href'], $element['#localized_options']);
      }
    
      return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
    }
    

    On each menu item, the links are such as http://#main-content.
    Hope this helps someone.

    skousha@gmail.com’s picture

    Hi,

    For some reason tb_wall_menu_item_link is not being called at all.

    I have the following function in template.php under my theme folder. Basically, when I refresh the page, I should see a blank page, while I see the contents in the page.

    function tb_wall_menu_item_link($item, $link_item) {
    exit;
    }

    I'm sure my theme name is right because I have the following function in the same template.php file and with exit function, page turns into blank page.

    tb_wall_youtube_thumbnail{
    exit;
    }

    alexfdg’s picture

    I´m not a code expert and I look for easy ways to solve issues without extra modules,
    so I found this solution, hope somebody helps: http://www.thecomputergroup.com/blog/how-to-make-a-drupal-menu-item-link...

    cyberzeus’s picture

    This is a great solution but the link is dead. Here is the method:

    1. Install the UUID & Menu Token modules
    2. Add this statement to each menu item you wish to have no link: [current-page:url]#
    3. Save & refresh page
    4. Test

    Good luck...

    giodesigns’s picture

    Thanks xtian7 your solution works.