Anchors in HTML are essentially bookmarks within a page that can be targeted directly by adding an anchor reference starting with '#' to the URL. The browser will then jump to the specified anchor.
However when a link targets an anchor on the same page the browser 'Back' button will no longer take the browser back to the previous page, but to the previous anchor.
What is an anchor
There are two ways of marking anchor points on the page. The old method is to use the <a> tag with a name attribute. It's a bit awkward as you can see:
<h2><a name="some-heading"></a>Some Heading</h2> <h2><a name="another-heading">Another Heading</a></h2>
The more recent technique is to assign an id where you want the anchor:
<h2 id="some-heading">Some Heading</h2>
In either case we can send the browser to that section of the page by appending the anchor to the URL using a hash (#):
For linking to an anchor on the same page we use:
<a href="#some-heading">Click here to jump to Some Heading</a> <a href="#another-heading">Click here to jump to Another Heading</a>
<form method="GET" action="#" onsubmit="return false;"> <select onchange="self.location.href = '#' + options[selectedIndex].value;"> <option>-- jump navigation --</option> <option value="some-heading">Some Heading</option> <option value="another-heading">Another Heading</option> </select> </form>
The result, which can be nicely styled using CSS, is the following:
You will see that when an option is selected the browser jumps to a new anchor point on the page and the anchor (#) appears in the URL.
In all the above examples the browser is taken to different parts of the page my modifiying the URL, and this makes the 'Back' button behaviour less intuitive.
What if we could keep the benefits of using anchor points, but remove the drawback of polluting the browser history?
The solution is to use the scrollIntoView method which is supported in all major browsers. It scrolls to the (any) selected element without changing the URL.
We still need to mark the anchor points using an id, but now the browser history remains unaffected.
Applying this to the SELECT navigation we end up with:
<form method="GET" action="#" onsubmit="return false;"> <select onchange="document.getElementById(options[selectedIndex].value).scrollIntoView(true);"> <option>-- jump navigation --</option> <option value="some-heading">Some Heading</option> <option value="another-heading">Another Heading</option> </select> </form>
The behaviour is the same, only that the browser URL stays the same.
Applying a polyfill
The following code will parse your HTML page and override the function of any links that target anchor points on the same page. The link function will be replaced with a call to the scrollIntoView method of the target element:
On page load the function loops through all links (<A>) on the page looking for those that have a hash in the target address AND that point to the current page (self.location.href).
We add an onclick event that overrides the default browser behaviour. The script assumes that all anchor points have been marked up using the id technique.
You can see the effect on these two links. They are marked up as normal links, but when the page loads our script 'upgrades' them:
Unlike before, you can trigger these links as often as you want, but the Back button will still go to the previous page and not the previous anchor point you've visited.
Extra code will be required to support IE8, as usual, and you might want to check that the target id actually exists before assigning the new handler to a link.
For modern browsers we can clean up the code a bit by switching to querySelectorAll for identifying links on the page:
This gives us the flexibility to restrict the function to a certain section of the page:
or to links with a specific class:
At the same time, we've set the behaviour of the scroll transition to 'smooth' which results in a smooth transition in supported browsers (all except for Internet Explorer and Safari). For more options, check out the links below.