20

I'm trying to use l() to get something like <a href='#' ...>, for ajax-calling purposes. https://stackoverflow.com/questions/1698453/drupal-creating-anchor-only-link-with-l is very close to the right thing -- use an option in l() like 'fragment' => 'foo' to produce a link like <a href='#foo'...> -- but I'm trying to get a clean '#'. Is there any way to do this, or should I just put something in as part of the fragment and not worry about it?

12

If you could not use clean "#" with fragment, I would suggest you to use <a href="javascript:">link</a>.
When adding "javascript:" or "javascript:void(0)" to href attribute, the <a> tag will do nothing. Then you can bind "onclick" to the link for your ajax purpose.
You can implement this using l() function as below:

     l('link', 'javascript:',  array('external' => TRUE));
  • what about javascript:void(0) – Serjas Nov 28 '12 at 9:59
  • 1
    @Serjas, javascript: and javascript:void(0) are the same. The only difference is "void(0)" :) – Sithu Nov 28 '12 at 10:37
  • I mean will l() convert ()? – Serjas Nov 28 '12 at 10:45
  • 6
    You should never ever use such shenanigans and just use event driven JavaScript! This is 2012 not 2004. Why is such a low quality answer up voted? Popular because it's easy? But it's extremely wrong. :( – user49 Dec 4 '12 at 18:47
  • @chx, as you are a drupal core maintainer, I hope you are not abusing not to use the second parameter to the l() function rather than path or fragment. Unfortunately, the function accepts "javascript" as a path :) If it is extremely wrong, why does Drupal accept it. Probably, it will be restricted in the later Drupal versions. – Sithu Dec 5 '12 at 11:10
15

PHP typecasting pop quiz time! (Full disclosure: I didn't win in our office).

As pointed out already, this is the code in l() that we're concerned with:

if (isset($options['fragment']) && $options['fragment'] !== '') {
  $options['fragment'] = '#' . $options['fragment'];
}

So here's the question: What value casts to an empty string but passes the following?

(isset($var) && $var !== '')

The answer is: FALSE!

So, we could try this:

l(t('My link'), NULL, array('fragment' => FALSE));

Which gives us:

<a href="/#">My link</a>

Thankfully we can strip that leading slash by passing along external => TRUE:

l(t('My link'), NULL, array('external' => TRUE, 'fragment' => FALSE));

Which gives us:

<a href="#">My link</a>

Boom. The things we do to avoid concatenating html strings manually, eh?

Postscript:

I'm not sure I'd actually recommend printing a link like this from php under normal circumstances. Without your javascript, all that link is going to do it jump your user to the top of the page (which, to my slight surprise, is specced behaviour). It should probably point somewhere that works without javascript.

If the link really only makes sense with javascript around (like the controls for a js-only slideshow or something), I think it's better to add the link to the page with javascript. Alternatively, if you really want to put it in your source, make sure it's display:none'd by default. Drupal has the .js class on the html for then showing it when javascript is around.

7

Unfortunately the url() function (which l() calls internally) pretty much nukes any chance of an empty fragment with the following lines:

if (isset($options['fragment']) && $options['fragment'] !== '') {
  $options['fragment'] = '#' . $options['fragment'];
}

It checks to make sure the fragment isn't an empty string, which is what you'd need to pass to get the clean #. I think the easiest solution would be to pick the id of a suitable element somewhere in the page and use that for the fragment instead (or javascript:void as Sithu has mentioned).

However...the most accessible way (and subsequently, in many countries, legal way; think section 508 in the States, the DDA in the UK, etc.) would be to provide the content that's being loaded/adjusted by your link on a separate page, and use the URL for that page as the link's href instead. You would then use javascript to stop the event on the link from actually redirecting the page for those browsers which have it enabled.

That way you have fallback content for users without javascript, you stay legal, and you keep everyone and their dog happy :)

  • Great edit about fallback! If we use the ajax URL in the link's href, can the calling page be a renderable HTML if javascript is disabled? I ever had print drupal_json_output($output) in my ajax calling page. – Sithu Nov 29 '12 at 3:15
4

You could't use clean #.

But it's possible to add space symbol after #:

  l('Link title', '# ', array('external' => TRUE));
2

I just had the same issue, but none of the solutions were satisfying, so i came up with my own one:

l('link', '', array('fragment' => ' ', 'html' => TRUE, 'external' => TRUE));

This produces a clean #

<a href="#">link</a>
  • Close, this produces <a href="# ">link</a> for me. – tyler.frankenstein Sep 20 '18 at 3:07
1

I would use render type html_tag instead because I believe render link receive special attention from Drupal. The downside being there is no short-cut function for it, so we'll have to use a render array:

array (
  '#type' => 'html_tag',
  '#tag' => 'a',
  '#attributes' => array (
    'href' => '#',
  ),
);
0

This is basically the same answer I posted here... Setting fragment to a single space will convert to a #, and $current_path is included because otherwise the link will just be set to www.example.com/# instead of www.example.com/current-page#:

$current_path = check_url(drupal_get_path_alias($_GET['q']));
$link = l('link', $current_path, array('fragment' => ' '));
  • There is no need to get the path alias because l() will convert to alias for you. It is actually preferable to use a non-aliased path. – rooby Mar 4 '15 at 13:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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