Chris Down
Chris Down

Reputation: 1795

Make anchor links refer to the current page when using <base>

When I use the HTML <base> tag to define a base URL for all relative links on a page, anchor links also refer directly to the base URL. Is there a way to set the base URL that would still allow anchor links to refer to the currently open page?

For example, if I have a page at http://example.com/foo/:


Current behaviour:

<base href="http://example.com/" />
<a href="bar/">bar</a> <!-- Links to "http://example.com/bar/" -->
<a href="#baz">baz</a> <!-- Links to "http://example.com/#baz" -->

Desired behaviour:

<base href="http://example.com/" />
<a href="bar/">bar</a> <!-- Links to "http://example.com/bar/" -->
<a href="#baz">baz</a> <!-- Links to "http://example.com/foo/#baz" -->

Upvotes: 67

Views: 34403

Answers (13)

Andrey  Chesnakov
Andrey Chesnakov

Reputation: 170

Building upon Joram van den Boezem's answer, but in plain javascript:

document.addEventListener('DOMContentLoaded', function(){
    let pathname = location.href.split('#')[0];
    [].forEach.call(document.querySelectorAll("a[href^='#']"), function(a) {
        a.href = pathname + a.getAttribute("href");
    });
});

Upvotes: 0

Abhi Beckert
Abhi Beckert

Reputation: 33349

My approach is to search for all links to an anchor, and prefix them with the document URL.

This only requires JavaScript on the initial page load and preserves browser features like opening links in a new tab. It also and doesn't depend on jQuery, etc.

document.addEventListener('DOMContentLoaded', function() {
  // Get the current URL, removing any fragment
  var documentUrl = document.location.href.replace(/#.*$/, '')

  // Iterate through all links
  var linkEls = document.getElementsByTagName('A')
  for (var linkIndex = 0; linkIndex < linkEls.length; linkIndex++) {
    var linkEl = linkEls[linkIndex]

    // Ignore links that don't begin with #
    if (!linkEl.getAttribute('href').match(/^#/)) {
      continue;
    }

    // Convert to an absolute URL
    linkEl.setAttribute('href', documentUrl + linkEl.getAttribute('href'))
  }
})

Upvotes: 1

OuzoPower
OuzoPower

Reputation: 278

You can use some JavaScript code inside the tag that links.

<span onclick="javascript:var mytarget=((document.location.href.indexOf('#')==-1)? document.location.href + '#destination_anchor' : document.location.href);document.location.href=mytarget;return false;" style="display:inline-block;border:1px solid;border-radius:0.3rem"
 >Text of link</span>

How does it work when the user clicks?

  1. First it checks if the anchor (#) is already present in the URL. The condition is tested before the "?" sign. This is to avoid the anchor being added twice in the URL if the user clicks again the same link, since the redirection then wouldn't work.
  2. If there is sharp sign (#) in the existing URL, the anchor is appended to it and the result is saved in the mytarget variable. Else, keep the page URL unchanged.
  3. Lastly, go to the (modified or unchanged) URL stored by the mytarget variable.

Instead of <span>, you can also use <div> or even <a> tags. I would suggest avoiding <a> in order to avoid any unwanted redirection if JavaScript is disabled or not working, and emulate the look of your <a> tag with some CSS styling.

If, despite this, you want to use the <a> tag, don't forget adding return false; at the end of the JavaScript code and set the href attribute like this <a onclick="here the JavaScript code;return false;" href="javascript:return false;">...</a>.

Upvotes: 0

davide bubz
davide bubz

Reputation: 1329

I found a solution on this site: using-base-href-with-anchors that doesn't require jQuery, and here is a working snippet:

<base href="https://example.com/">

<a href="/test">/test</a>
<a href="javascript:;" onclick="document.location.hash='test';">Anchor</a>

Or without inline JavaScript, something like this:

document.addEventListener('DOMContentLoaded', function(){
  var es = document.getElementsByTagName('a')
  for(var i=0; i<es.length; i++){
    es[i].addEventListener('click', function(e) {
      e.preventDefault()
      document.location.hash = e.target.getAttribute('href')
    })
  }
})

Upvotes: 33

Steve Kang
Steve Kang

Reputation: 1

From the example given in the question. To achieve the desired behavior, I do not see the need of using a "base" tag at all.

The page is at http://example.com/foo/

The below code will give the desired behaviour:

<a href="/bar/">bar</a> <!-- Links to "http://example.com/bar/" -->
<a href="#baz">baz</a> <!-- Links to "http://example.com/foo/#baz" -->

The trick is to use "/" at the beginning of string href="/bar/".

Upvotes: -1

atoms
atoms

Reputation: 3093

You could also provide an absolute URL:

<base href="https://example.com/">
<a href="/test#test">test</a>

Rather than this

<a href="#test">test</a>

Upvotes: 4

CTarczon
CTarczon

Reputation: 898

If you're using Angular 2 or later (and just targeting the web), you can do this:

File component.ts

document = document; // Make document available in template

File component.html

<a [href]="document.location.pathname + '#' + anchorName">Click Here</a>

Upvotes: -2

Yektaweb Company
Yektaweb Company

Reputation: 49

To prevent multiple #s in a URL:

document.addEventListener("click", function(event) {
  var element = event.target;
  if (element.tagName.toLowerCase() == "a" &&
    element.getAttribute("href").indexOf("#") === 0) {
    my_href = location.href + element.getAttribute("href");
    my_href = my_href.replace(/#+/g, '#');
    element.href = my_href;
  }
});

Upvotes: 1

Michał Perłakowski
Michał Perłakowski

Reputation: 92471

If you use PHP, you can use following function to generate anchor links:

function generateAnchorLink($anchor) {
  $currentURL = "//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
  $escaped = htmlspecialchars($currentURL, ENT_QUOTES, 'UTF-8');
  return $escaped . '#' . $anchor;
}

Use it in the code like that:

<a href="<?php echo generateAnchorLink("baz"); ?>">baz</a>

Upvotes: 2

contendia
contendia

Reputation: 161

Here's an even shorter, jQuery based version I use in a production environment, and it works well for me.

$().ready(function() {
  $("a[href^='\#']").each(function() {
    this.href = location.href.split("#")[0] + '#' + this.href.substr(this.href.indexOf('#')+1);
  });
});

Upvotes: 3

Joram van den Boezem
Joram van den Boezem

Reputation: 1102

Building upon James Tomasino's answer, this one is slightly more efficient, solves a bug with double hashes in the URL and a syntax error.

$(document).ready(function() {
    var pathname = window.location.href.split('#')[0];
    $('a[href^="#"]').each(function() {
        var $this = $(this),
            link = $this.attr('href');
        $this.attr('href', pathname + link);
    });
});

Upvotes: 14

Michał Perłakowski
Michał Perłakowski

Reputation: 92471

I'm afraid there is no way to solve this without any server-side or browser-side script. You can try the following plain JavaScript (without jQuery) implementation:

document.addEventListener("click", function(event) {
  var element = event.target;
  if (element.tagName.toLowerCase() == "a" && 
      element.getAttribute("href").indexOf("#") === 0) {
    element.href = location.href + element.getAttribute("href");
  }
});
<base href="https://example.com/">

<a href="/test">/test</a>
<a href="#test">#test</a>

It also works (unlike the other answers) for dynamically generated (i.e. created with JavaScript) a elements.

Upvotes: 3

James Tomasino
James Tomasino

Reputation: 3581

A little bit of jQuery could probably help you with that. Although base href is working as desired, if you want your links beginning with an anchor (#) to be totally relative, you could hijack all links, check the href property for those starting with #, and rebuild them using the current URL.

$(document).ready(function () {
    var pathname = window.location.href;
    $('a').each(function () {
       var link = $(this).attr('href');
       if (link.substr(0,1) == "#") {
           $(this).attr('href', pathname + link);
       }
    });
}

Upvotes: 9

Related Questions