It is often desirable to create a menu item that links to a particular place on a page, e.g. /node/xx#name

In the menu system the # character is translated into %2523 and the link fails.

There is a hack, documented at http://drupal.org/node/106821, whereby you can specify a complete url to avoid this translation. This is an inelegant workaround and not a solution as it leads to management problems when the base url is not constant throughout the life of a site (e.g. when migrating between test/live sites or when it runs concurrently on two domains: xx.com, xx.co.uk).

I can't see any reason why the # character cannot be escaped. It doesn't pose any security threat and only users with admin privileges can define menu paths.

Files: 
CommentFileSizeAuthor
#60 123103-anchors-in-menu-items-2.patch2.4 KBmgifford
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch 123103-anchors-in-menu-items-2.patch.
[ View ]
#49 123103-anchors-in-menu-items.patch2.4 KBGarrett Albright
FAILED: [[SimpleTest]]: [MySQL] 18,946 pass(es), 4 fail(s), and 0 exception(es).
[ View ]
#39 anchors-in-paths.patch1.28 KBGarrett Albright
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch anchors-in-paths.patch.
[ View ]
#1 fragment.patch2.22 KBJohnAlbin
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch fragment.patch.
[ View ]

Comments

Category:feature» bug
Status:Active» Needs review
StatusFileSize
new2.22 KB
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch fragment.patch.
[ View ]

Named anchors are a standard part of the URL scheme. So not properly handling them in the menu system is a bug, IMO.

The attached patch allows admins to add custom menu items with relative URLs and anchor fragments.

(Also, I’ve updated the above handbook page to show a better workaround then having to enter full URLs.)

Status:Needs review» Needs work

Tweaks like this will not work afaik, since normal menu paths can contain the # character.

If we want to change the menu system to support fragments and/or query strings, this needs to be added properly, without introducing forbidden characters in the menu paths.

This could mean that you enter menu paths in urlencoded form (i.e; what this patch tries to do) in the UI, but that would need to be integrated cleanly with the current unencoded, module-generated paths.

The path_redirect module adds an extra field in for named anchors

_____________ # _____________

or similar. That makes the distinction pretty clear, not sure how it stores it though.

Status:Needs work» Needs review

From section 3.3 of RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax):

The path is terminated by the first question mark ("?") or number sign ("#") character, or by the end of the URI.

So the # and ? characters, according to the RFC, are already forbidden in the path. Right now, Drupal encodes all characters in the path (including the #), but I can’t think of any real-world examples of using an encoded # in the path of a URL.

Steven, the above patch doesn’t change the way Drupal handles url encoding. You would still enter URLs in unencoded form. It just splits the path at the first # and puts the parts back in $item->path and $item->fragment.

Status:Needs review» Needs work

I'm not talking about URL path components, but about Drupal menu paths. They are not the same.

As for a real world example... any search that includes a # in the keyword box.

Any progress on this lately?? Was there a group consensus reached on a course of action??

I've needed this for a very long time and am amazed it's still not possible. This is a pretty basic thing to want to do in a web site. The example I need it for right now is a political candidate's bio page with targets to each section (why I'm running, voice of the voters, etc.). I'd like to not have to break the bio up into three pages simply because the menu won;t let me use targets.

Anchors are necessary. We using them to separate paragraphs in a long article. As I create my own theme, I do not have any problems to insert a simple str_replace, but the time I used for the search.

I cannot imagine, why an url cannot have an anchor in the menu system. It is like an article without a title.

It's crazy not to support this.

I have lots of pages with a <a name='top'></a> at the top and a <a href='#top'>Top</a> at the bottom.

I can't do this in Drupal.

In v5.x, I hacked the url and menu_path_is_external functions so it worked, by just adding:

  if(strpos($path, '#') === 0) return $path;

as the first line in each of the functions, but in 6.x, the changes to the menu system break that simple hack.

Can we revive this thread? There seems to be a consensus that enabling # and ? in the menu system does make sense (I just wanted to link to a Drupal page like "my/url?page=2", which is not allowed unless you specify the FQDN). Can some developer look at this, please?

Thanks,
Stephan

Version:5.x-dev» 6.x-dev
Status:Needs work» Fixed

I believe this is fixed in 6.x.

Status:Fixed» Closed (fixed)

Automatically closed -- issue fixed for two weeks with no activity.

i have 6 and i am having the same prob. i dont think its fixed yet. anyone figure this out yet?

mike

Yeah, this seems kind of strange. What about the fairly usual case where a menu item should point to something with a url redirect after in the form of: ?destination=

At the moment in Drupal 5 (haven't tested 6) it wont work. In many other modules and situations we can specify a destination in the url, why not in a menu item?

Status:Closed (fixed)» Needs work

I don't think this was fixed in 6.

Relative paths using named anchor links in menu items do not work. The menu system won't accept them as a valid path.

Status:Needs work» Postponed (maintainer needs more info)

@Ryan - if you really think this needs to be re-open, please more details of how you'd solve this. For example, we might think about a feature where a menu link could use <current> the same way it uses (at the moment) <front>

I realize the menu navigation is attached at the hip with hook_menu and the page serving mechanisms of Drupal, and that the needs of this cannot be ignored when requesting an issue that seems to relate to only adding menu items to the tree, not defining paths in the first place. My question is, is it really practical for hash symbols to be used in paths, and should this character become a non-eligible character altogether for the purposes of Drupal?

Assuming we must have hashes in paths without them being links to anchors (ie, encoded), how about:
- another column in the menu_links table (fragment)
- another field for fragment in menu_edit_item form, or some means of denoting a fragment (eg. "node/123bottom")

This would let everything else (# symbols and the rest) be properly encoded, and fragments could be built into the links when the menus are build.

I may not have the insight to suggest such radical changes to Drupal core at this point, though.

@Ryan - for Drupal 6, anchors should already work for "normal" links. I thought the only issue to be addressed here was named anchors for the current page.

To my knowledge, with the exception of comment #17, this issue has been regarding menu items containing named anchors, WITH Drupal paths as well. Being able to leave the pre-anchor part of the path blank to have it always apply to the current page would be a bonus, IMHO.

Example: When adding a menu item with a path "buy#1", I get the following response:

The path 'buy' is either invalid or you do not have access to it.

Category:bug» support

um, then I would expect you get the same error message inserting a link with the path "buy", right?

I can add a link to the path "node/4#hello" and it works fine.

Interesting.

For me, "buy" was an alias to node/40. Normally, when entering an alias to a menu item path, it replaces it with the node/# equivalent and notifies the user of the change, but in this case it says the "buy#1" path is not valid.

The menu item accepted a path of node/40#1 and the menu item ends up being buy#1 which is great.

Does anyone know, is this by design? And if so, should something be added to the description of the menu path field specifying this feature/limitation?

hmm, accepting aliases at all was added late, so this may indeeed be a bug - please investigate and refocus the issue.

I can confirm I have what looks like the same issue as Ryan, where the named anchor will work only if referencing it via node name, but not the alias.

Category:support» bug

I agree with pwolanin (#19 and #23), the only issue left 6.x is anchor for path aliases.

  • node/1#anchor works just fine.
  • my-nice-alias-to-node-one#anchor does not work. Error given when trying to save the menu item with this path :
    • The path 'test' is either invalid or you do not have access to it. When I remove the anchor from this path, the menu item is created well (because the path does exists).

And while fixing this I also think there are some thought to put in path menu items consiting only of an anchor (starting with #) which should "point" to the current path. I don't think a "link" (working like ) is useful, it is possible to just check if the path starts with # and if it does just use the specified path as the link (as it would do with an external link), then for browsers the link would be a relative link to an anchor on the current page.

When you add a menu item for node/1#anchor - does the path get aliased when you view it in the menu?

Yes it does get aliased and the anchor is working. Then, maybe it needs to be documented, although I know that this "translation" would certainly occur it didn't come to my mind to do it this way. Thanks for the solution.

Seeing that I am not the only one who thought that using directly the alias with an anchor, and not the nid, maybe it would not be such a bad idea to offer both solution for anchors.

Lastly, that leaves us with the feature request, the "relative anchor" I talked about before, maybe we should create a feature request for this ?

Version:6.x-dev» 7.x-dev
Status:Postponed (maintainer needs more info)» Active

OK, marking this back to active and bumping to 7.x. I can see two issues here here:

1. allowing a menu item with only a fragment, i.e. #anchor
2. When drupal_get_normal_path is called as part of the menu item validation, if the alias has an anchor appended, ignore this, then add it back to the normal path (so alias#anchor gets split to alias + #anchor which eventually gets saved as node/1#anchor).

I'm not sure how the first one should work - which use cases are there for having the same #anchor on every page a menu might show up on? #2 sounds like a decent usability improvement to me.

Category:bug» feature

About 1., an example would be "skip links" to improve accessibility. At the top of the page you offer links which point to named anchors like "#content" (if you have a named anchor or even an element with id called "content") and it will help people using a screenreader to skipping all the navigation links (for example). They will reach the content way more quickly. Of course this can be done directly in the page.tpl, but then you loose all the menu features. For example the ability to translate those. Well, it is possible to make this last part (transaltion) with the t() function and the interface translation system too.

If it is implemented, I doubt it would require much more efforts than it took to support external links... but with that kind of paths the "#" should not be encoded otherwise it will just not work of course.
Another problem we would run into is that the link_path field would be empty in the menu_links table since fragments (anchors) are stored in the options field, if I understood how it works, so after all the " <current>" placeholder wouldn't be such a bad idea. It could also be useful with querystring (which is stored in the same field, options) instead of anchor, the principle is the same for both (we would just need to respect the order, ?query=string#anchor (and not the opposite).

About 2., I would also see it as usability improvement more than a real "bug fix" (well, the feature is here, it just does not work as I expected, but it works), thus I changed the category. I think that your reasoning is right about how it should be done, it makes perfect sense since it basically how it works right now with not aliased paths (according to what I read this afternoon, so I might not have catched the whole picture but that's my understanding).

I left it as a bug, because usability issues are bugs ;) It either needs more documentation there, or the behaviour should be changed.

Hi Guys,

Just to echo a few points that people have already made, about why this is important.

In terms of accessibility, a "skip to content" link is extremely useful for blind users (using screen readers) as it gives them the ability to skip through all of your links and go straight to your main content. Here I would usually use

<a href="#maincontent">Skip to content</a>

but when this is published in a drupal menu the '#' becomes encoded as '%23' (don't quote me on that exact encode!)

The sooner this is sorted, the sooner I can stop worrying about hard coding in this skip links into my page.tpl.php file! I'm sure many of you feel the same.

Just thought I'd add a comment anyway!
Garry

This sounds like a new feature - we have a <front> special symbol now - maybe we need something like a <current> tag to make clear the intent?

Title:Allow named anchor links (#) in menu pathRetain #anchors during path alias -> normal path saving
Category:feature» bug

As far as I can see there's two issues:

1. If you save a normal path with a fragment, this is handled fine in the menu. When you enter a path alias, drupal_get_normal_path() doesn't validate it as an alias, so you can't save it. Since the end result would be the same, this seems like a UI inconsistency to me and it's clearly caused some confusion. (I haven't tested this, going on the reports above).

2. Being able to enter a fragment for the current path. <current> sounds like a reasonable way to do that. Although I can't think of a situation where you'd want to add anything other than a #fragment for the current path, so maybe just validation of the string might work?

These seem like different issues to me, so I've split current path handling to a new issue #325533: Allow <current>#fragment as a menu path

About 2., the only other occasion I see would be adding a querystring (as I said previously)... the use of it would also be limited but might still be useful, it can be handy to pass a parameter to the current page (i.e. like offering a link to the same page with a parameter telling the user does not have Javascript... indeed very limited, not that many other examples come to my mind right now ;)), but as I said it should not take that much more time to implement since it is done exactly the same way for #fragment and querystring (stored in options field in the menu_links table). The <current> would make it possible to handle both, as it is already done with <front>.

@catch - thanks for clarifying and splitting the 2 issues.

For those who really want to append an anchor, etc, in D6 to the current page, I suggest you'll need to implement/use a trivial module that implements hook_translated_menu_link_alter(). If I get bored I'll write it in the mini module section of the handbook.

I would like to create a menu item (using the admin interface) in a menu, say secondary-links, with the "Menu Link Title" as "Top" and the "Path" as "#top".
It would generate

<a href="#top">Top</a>

as the menu item. In this way, every page can have a "Top" link that works as expected.

But this is not possible today.

This seems to be linked to this whole discussion.

I've tried to raise this seemingly simple issue before, but have not gotten anything resembling a reasonable solution.

So, I thought I'd try posting this issue in this thread for you consideration.

Thanks,

Mitch

Hi Mitch,

I've been after this for a while - it's been moved to here http://drupal.org/node/325533 but the solution at the bottom of the page still doesn't work for me - kind of annoying as its really good for accessibility - hopefully it won't go unnoticed.

Garry.

StatusFileSize
new1.28 KB
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch anchors-in-paths.patch.
[ View ]

Here's a VERY LIGHTLY TESTED patch for D6 which fixes the alias#anchor problem mentioned in #20 and #21. As I mentioned, it's lightly tested, so do NOT put this on a live server, but please test it otherwise if you can.

It does not allow you to something like simply #top as a path - in fact, the result if you try that is rather odd - it links to the settings page for the current theme. (What the…?)

I'll try to look in to getting a D7 patch for this hacked together by tomorrow evening, if nobody else does it first. Again, please test this patch with D6 if you can so I can be sure I'm on the right track before attempting to submit a D7 patch.

A quick test of the patch in #39 worked for me. Can't speak to the ramifications but it fixed the immediate problem.

Ack, I totally forgot I promised to work on this… Thanks for the bump.

Issue tags:+accessibility

tagging

Version:7.x-dev» 6.15

I went about it the same way as #25 & #27 TheRec & #26 catch when getting a Menu item to work while including a hash #anchor tag in D6.15.

I have a parent Menu item, Computers, with two child Menu items, Classes & Lab. I wanted the page, Computers, to include Classes & Lab sections with anchors Classes & Lab. For the Menu item Classes, the alias was not accepted when entered into the Menu path like so--computers#Classes, but did work when the nid was entered like this--node/601#Classes. The nid with the hash tag was aliased and the anchor is working. Seems there needs to be documentation on this though.

Version:6.15» 7.x-dev

Please don't change versions, bugs are fixed in the development version first then backported.

Subscribing.

I'd like to see this feature. Might help address an issue where someone wanted to put in a "skip to main content" link in the primary menu too. Can't remember off hand what the issue ID for that was.

StatusFileSize
new2.4 KB
FAILED: [[SimpleTest]]: [MySQL] 18,946 pass(es), 4 fail(s), and 0 exception(es).
[ View ]

Over a month late, here's a D7 patch. It's pretty much equivalent to the D6 one. Seems to work.

Status:Active» Needs review

Status:Needs review» Needs work

The last submitted patch, 123103-anchors-in-menu-items.patch, failed testing.

Okay, the tests where this is failing are basically creating a test path with "unusual" characters, including the # character, and then trying to access a page through that path. I'm not sure what to do about that - looking through relevant RFCs, it appears to not be incorrect to allow the # character in a legitimate path, so long as it's encoded. But it makes life tough for us in this case, because how do we distinguish between a # in a legit path and a # that denotes an anchor fragment?

We may not be able to solve this and #325533: Allow <current>#fragment as a menu path unless we stop allowing # in created paths. I may create an issue about that later after a bit more consideration.

Okay, well, to thoroughly confuse the matter, I'm getting these same test results locally if I use a virgin core CVS pull with a virgin database. Can anyone else replicate this, or is it just me?

I'm so confused. It seems to me like that if this patch were to cause tests to fail, those failing tests are likely ones to fail. Oh the other hand, I can no longer get those tests to not fail using a completely unpatched HEAD no matter what server and configuration I try it with. On the other other hand, other patches seem to be running past the testbots on the testing servers without these tests failing.

I can't believe I've wasted so much time on this. Giving this up until at least tomorrow or until someone smarter than me can step in and figure out what the hell is going on.

(Side note: cvs co -C should check out HEAD while overwriting all of your core hacks, right? Not that it matters in this case - I still get this problem if I baleet my repository and do a completely fresh checkout or even install the -dev tarball.)

#759808: Disallow # character in path aliases will help us move forward on this.

VERY interested in this. Therefore... subscribing. :)

Subscribing

subscribing

Ok, have to look at patch in #49. Is it just a matter of changing path.test so that the tests

Changed alias works. & HTTP response expected 200, actual 404 using PathTestCase->testAdminAlias()

Status:Needs work» Needs review
StatusFileSize
new2.4 KB
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch 123103-anchors-in-menu-items-2.patch.
[ View ]

trying to keep up with core

Status:Needs review» Needs work
Issue tags:-accessibility

The last submitted patch, 123103-anchors-in-menu-items-2.patch, failed testing.

Status:Needs work» Needs review
Issue tags:+accessibility

#39: anchors-in-paths.patch queued for re-testing.

Ok, so this is the two places where this patch fails in modules/path/path.test

    // Confirm that the alias works.
    $previous = $edit['alias'];
    $edit['alias'] = "- ._~!$'"()*@[]?&+#%,;=:" . // "Special" ASCII characters.
      "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
      "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
    $this->drupalPost('admin/config/search/path/edit/' . $pid, $edit, t('Save'));
    // Confirm that the alias works.
    $this->drupalGet($edit['alias']);
    $this->assertText($node1->title, 'Changed alias works.');
    $this->assertResponse(200);

    // Change alias to one containing "exotic" characters.
    $previous = $edit['path[alias]'];
    $edit['path[alias]'] = "- ._~!$'"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
      "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
      "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
    $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save'));
    // Confirm that the alias works.
    $this->drupalGet($edit['path[alias]']);
    $this->assertText($node1->title, 'Changed alias works.');
    $this->assertResponse(200);

I'm tempted to just nix the # from the testing string, but I do wonder what happens when someone puts in a # into the title of their node. We should test for this use case, right?

Issue tags:+Simpletest

Adding simpletest tag. This again is waiting for fixes to the test.

Subscribing

Issue tags:-accessibility, -Simpletest

#60: 123103-anchors-in-menu-items-2.patch queued for re-testing.

Status:Needs review» Needs work
Issue tags:+accessibility, +Simpletest

The last submitted patch, 123103-anchors-in-menu-items-2.patch, failed testing.

Version:7.x-dev» 8.x-dev
Issue tags:-accessibility

Bumping to D8 and removing the accessibility tag. I don't really see where this would be any more useful from an accessibility perspective than it would be in general. Putting skip links in the page.tpl.php file is pretty standard, I don't really see any advantage at all of running these through the menu system, perhaps someone can correct me if I am wrong.

Possible stupid question: Would it be possible to fix this in contrib for D6/D7 or is this something that needs to be done in core?

I just noticed if I do an address like
node/16#gohere
the auto url will change it to be like
about-us#gohere

This is in D7.

Subscribing

I am struggling for more than 4 weeks with this problem, nobody is answering my question and I can not find any solution on this site nor the internet. I tried some many patches but non of them is working. Why does the url alias return a %23 instead of a #. I would like to go, from my search results, to an id (anchor) in long panel pages with lists of products. It is essential for my customers that they see the product they searched for but also the related products on that panel page. Does anyone has the final solution, if I need modules to do this please give me a hint.

Just a quick note to hopefully save someone else the frustration I just went through. If you are creating a menu item which links to a named anchor in d7 it works if you set the path to node/nodenumber instead of the alias.

For example if you try to set the path to about-us#gohere and try to submit the form it will say "The path 'about-us' is either invalid or you do not have access to it.", whereas setting the path to node/2#gohere works fine.

I recently had to built a site for a client that required anchor links on the main menu as the whole site was on a single page.
Admittadely to organise the code better I externally linked each page, even though essentially they were all on the one node it was easier to maintain include files.

I simply added menu links with the path:

<front>#about-us
<front>#services
<front>#contact

I didn't read the post but thought I'd share my experience anyway! :P

But what can i do, when # is replaced with %23

Version:8.x-dev» 7.12

Has there been any further updates on this issue for Drupal 7? I want to be able to use the alias and not the node number when linking to an anchor. Sorry for the version change - unintentional quick mouse.

Version:7.12» 8.x-dev

Changed back to 8-x-dev

Not sure if anyone would be interested in a 'fix' using jquery but this might work also (I have all 3 linking to the same "policy" page)

$(document).ready(function() {
  var _href = "";
  //Privacy Link
  _href = $("#block-menu-menu-footer-menu a:contains('Privacy Policy')").attr("href");
  $("#block-menu-menu-footer-menu a:contains('Privacy Policy')").attr("href", _href +"#privacy");
  //Returns Link
  _href = $("#block-menu-menu-footer-menu a:contains('Returns Policy')").attr("href");
  $("#block-menu-menu-footer-menu a:contains('Returns Policy')").attr("href", _href +"#returns");
  //Shipping Link
  _href = $("#block-menu-menu-footer-menu a:contains('Shipping Policy')").attr("href");
  $("#block-menu-menu-footer-menu a:contains('Shipping Policy')").attr("href", _href +"#shipping");
  });

@gynekolog I can only imagine that you would enable pathauto and automatic alias the node so you don't get %23.

:)

Hm. I'm going to expand on this one. I've noticed something illogical. Take a look at these 5 url's. Try to add them as a menu item. Asssume that 'test' is an URL alias for node/1

1. node/1: Works
2. test: Works. But it will convert the URL alias to an internal path
3. node/1#test Works. Oddly enough.
4. test#test Fails with a cryptic "The path 'test' is either invalid or you do not have access to it."
5. http://google.com. Works. Because this is an external URL.

The last case is peculiar as test is a valid, existing URL alias. So why is it failing?

Take a look at MenuLinkFormController->validate().

First the validation goes through $normal_path = drupal_container()->get('path.alias_manager.cached')->getSystemPath($menu_link->link_path); to get the normal (internal) path of the item, assuming that this is an URL alias (of course). Since test#test is considered as a whole rather then as an URL alias with a fragment, it will fail and assume that test#test is an internal path in itself and the conversion code will just return what it was fed: test#test. Then it will check if the URL is external. Of course, it isn't. Finally it arrives here:

    if (!trim($menu_link->link_path) || !drupal_valid_path($menu_link->link_path, TRUE)) {
      form_set_error('link_path', t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $menu_link->link_path)));
    }

drupal_valid_path() will fail since test#test is an anchored URL alias while the system, at this point, assumes it's just another internal path. This is where the validation fails to recognize a use case. It works perfectly for paths which aren't URL aliasses or recognized as internal paths. But it doesn't take fragments into account.

It's either a problem with the validation here or rather an issue lower down the chain. Probably when the getSystemPath() method is called.

Anyhow, I stumbled into this today while working on a D7 project which deals with a lot of anchored links structured in a menu. Since anchored internal paths *do* get saved but not anchored URL aliasses, this constitutes as bad UX towards end users imho: the only way to grasp this limitation is knowing what the difference is between an alias and an internal path.