Using named anchors with menus
Last modified: June 8, 2007 - 00:14
Users who have tried using “named anchors,” also known as “jump links,” in the menu system can run into problems with them not to working. For clarification, a named anchor looks like this:
<a name="anchor"></a>
The expected behavior is that when a link that points to this named anchor is clicked, the user’s browswer should jump to a to the anchor tag on the html page, making it appear at the top of the user’s browser window.
This desired behavior can be accomplished by adding the following function to your theme’s template.php file:
function phptemplate_menu_item_link($item, $link_item) {
// Convert anchors in path to proper fragment
$path = explode('#', $link_item['path'], 2);
$fragment = !empty($path[1]) ? $path[1] : NULL;
$path = $path[0];
return l(
$item['title'],
$path,
!empty($item['description']) ? array('title' => $item['description']) : array(),
!empty($item['query']) ? $item['query'] : NULL,
$fragment,
FALSE,
FALSE
);
}
Code not necessary
As drupal outputs strict xhtml 1.1 named anchors ie.
<a name="anchor"></a>do not work.They have been deprecated in xhtml 1.1 and replaced with the id tag. The id can be used on any tag eg.
<p id="anchor"></p>and then the anchor can be called as you would previously ie.To get the links to the anchors working in your menu you need the full url as the link ie.
not
The above code is not necessary for this to work.
good point about id's but.....
I had forgotten that named anchors were deprecated, so, thanks for reminding me. Much cleaner anyhow.
However, absolute urls are not viable for a production site. The whole point of a framework (and cms in drupal's case) is to abstract as much as possible in order to make the application portable and scalable. I agree with others that a permanent solution for named anchors in Drupal 5.x menus would be greatly appreciated. Until then, I will try this template override.
thanks,
Brian
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
http://www.terraeclipse.com -- where I work
http://www.brianthomaslink -- where I will someday get around to playing
Code not necessary
Thank you rmyoung, good point. I had forgotten and wasted hours. Very good point.
Code doesn't seem to work
Hi,
I'm very interested in this as it is a great feature when trying to improve the accessibility of your website. But the code doesn't appear to do anything, I entered "#content" into my menu item Path and the link remains as
http:///example.com/%23content
instead of:
http:///example.com/#content
And also I am interested in how this has become deprecated? Where would I read up on this?
Thanks, Garry.
Don't know if it was just a
Don't know if it was just a typo but you have too many slashes after http. The snippet for the template file only works for named anchors. If you are using an id such as
#contentyou need to have the full path in your menu item ie.http://example.com#commentsNOT#comments.Here's a page from w3c schools explaining that it's deprecated: http://www.w3schools.com/xhtml/xhtml_syntax.asp
Typo was an accident!
Sorry about the typo, that was an accident!
Either way, putting in a whole path into the menu does not solve the issue. This link should be on every page so that users can skip to content, for example,
On the homepage:
http://www.example.com/#content
On the contact page:
http://www.example.com/contact#content etc...
This is vital for users with screenreaders who want to skip to main content, thus I think it's important that it is made available in the drupal menu items
Not deprecated
I don't think the code is deprecated. I think it should be marked for 6.x as well (or an updated version should be posted). I had a similar problem with hook_link, but hook_link directly calls the l() function and it has a 'fragment' option specifically for handling anchors (see http://api.drupal.org/api/function/l for details), so you can do something like this:
<?php
/**
* Implementation of hook_link().
*/
function drivingforce_features_link($type, $node = NULL, $teaser = FALSE) {
$links = array();
if ($type == 'node' && $node->type == 'story' && is_numeric(arg(1))) {
$links['node_top'] = array(
'title' => t('Top'),
// link node back to itself - path module will sort out any aliases for us
'href' => 'node/' . $node->nid,
// this line will properly attach #top-anchor to the end of our URL
'fragment' => 'top-anchor',
'attributes' => array('title' => t("Jump back to the top of the page.")),
);
}
return $links;
}
?>
Then all you need to do is add an id of 'top-anchor' to the h1 tag in the page.tpl.php template.
HOWEVER, hook_menu still has no 'fragment' option itself. The template.php code above mimicks the 'fragment' behaviour of the l() function for hook_menu and re-builds the link using the l() function if it finds a # in the path.
AFAIK, this is still valid. There's nothing in the current hook_menu docs referring to 'fragments', nor is there any reference to a similar mechanism in Drupal 7.x hook_menu docs, so it seems to me this will continue to be valid for a few years. If it doesn't work with Drupal 6.x, it probably just needs a tiny tweak to make it work, but it *is* required. As te-brian said, absolute URLs are not a viable solution. =(
Has anyone raised this as a D7 feature request for the menu system, btw??
anchors in menus of D6
Just a note to add here, as it didn't seem clear to me:
But if you want to add items (perhaps sub items) to your menus that are actually anchors within existing pages, it only seems to work in Drupal 6, if you specify the path as node/number#anchor version of the node, and not the alias.
For example, if you have a page: http://example.com/services
which was actually http://example.com/node/5
and you wanted a menu item to an anchor on that page, e.g. #maintenance
then you would need to add the item to your menu with the path
node/5#maintenanceThe menu saves fine, and in things like local menu and sitemaps, the aliased URL and anchor still translates properly.