Hey,
I currently have a link that links to a different part of the same page using <a href="#title"> which is defined using <a name="#title">. It all works fine but when clicked it adds #title to the end of the URL and when the user clicks the back button it goes back to when they clicked the link rather than actually going back a page which is very inconvenient for my site.

I have seen that on Facebook this has been done so I know there is a way, I just don't know how or what exactly to search on Google.

Any help much appreciated.
Thanks,
Tom.

Dani AI

Generated

A couple of clarifications and a modern, accessible pattern you can drop in.

First, use an id on the target, not name and not including the # symbol. The name attribute for anchors is deprecated; id is the current way to define in-page targets. Example: <h2 id="title">...</h2>. (developer.mozilla.org)

Building on ’s idea (replace the history entry) and ’s smooth-scrolling direction, you can keep normal anchor markup for fallback, intercept the click, scroll to the target, then remove the hash from the URL without creating a new history entry. This keeps the Back button behavior you want and hides #myAnchor in the address bar.

<a href="#title" class="no-history">Jump to title</a>
<h2 id="title">Title</h2>

<style>
  html { scroll-behavior: smooth; } /* optional smooth scroll */
</style>

<script>
document.addEventListener("click", (e) => {
  const link = e.target.closest('a.no-history[href^="#"]');
  if (!link) return;

  const id = link.getAttribute("href").slice(1);
  const target = document.getElementById(id);
  if (!target) return;

  e.preventDefault();
  target.scrollIntoView({ behavior: "smooth", block: "start" }); // or "auto"
  if (!target.hasAttribute("tabindex")) {
    target.setAttribute("tabindex", "-1");
    target.addEventListener("blur", () => target.removeAttribute("tabindex"), { once: true });
  }
  target.focus({ preventScroll: true }); // keep scroll position stable
  history.replaceState(null, "", location.pathname + location.search); // remove # without new entry
});
</script>

Why this works:

  • scrollIntoView moves the viewport to the element programmatically. (developer.mozilla.org)
  • history.replaceState updates the current history entry instead of adding one, so Back goes to the previous page. (developer.mozilla.org)
  • scroll-behavior: smooth gives you smooth scrolling without jQuery. (developer.mozilla.org)
  • Setting focus (with preventScroll) preserves accessibility for keyboard/screen reader users without re-scrolling the page. (developer.mozilla.org)

Tip: If you have a fixed header, add scroll-margin-top on your targets to avoid them hiding under the header.

Recommended Answers

All 6 Replies

Tom,

The following should work:

function hashNoHistory(link){
  var a = document.createElement("a");
  a.href = link.href;
  location.replace(a.href);
  return false;
}
<a href="#myAnchor" onclick="return hashNoHistory(this)">click me</a>
...
<a id="myAnchor">myAnchor</a>

However, Opera doesn't obey location.replace() under these circumstances. It's a known bug that they have failed to address for some time now. As far as I know the other major browsers work properly.

I understand there's a completely different approach to anchors in HTML 5 but I haven't had time to research it.

Airshow

Second thoughts, the function can be simplified to :

function hashNoHistory(link){
  location.replace(link.href);
  return false;
}

Unfortunately, Opera still won't play ball.

Airshow

Thanks for the help,

This works beautifully in stopping the history returning to the same page! Is there a way to stop the ugly "website.com/page#myAnchor" in the URL also?

Thanks alot,
Tom.

Tom, yes indeed there is.

First, there's no need to set the whole url in the href, just the #hash part. The browser assumes the rest of the url to be the same as the current page, which is exactly what you want.

Next, a particularly elegant solution is to set a class on each link of this type then attach click handlers with jQuery. You can avoid jQuery but it makes for very concise code.

$(function(){
  $(".noHistory").click(function(){
    location.replace(this.href);
    return false;
  });
});
<a href="#myAnchor1" class="noHistory">click me</a><br/>
<a href="#myAnchor2" class="noHistory">click me</a><br/>
<a href="#myAnchor3" class="noHistory">click me</a><br/>

This will actually work with any url (same domain, different domain, whatever you want), not just anchors on the same page. In all cases, by setting class="noHistory" and attaching the click handler, the latest entry in the browser's History object will be replaced and the Back button will work as you want (except in Opera as already mentioned).

Airshow

This is beautiful, got it working first time.

Thanks alot for all your help :)
Tom.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.