Change URL hash without page jump

In modern complex layouts, sometimes the point where a hash will transport you to will be entirely different than the one you actually wanted. If you prevent the default event, you will save yourself from the page jump, but the hash won’t change either. You can accept the regular behavior and change scrollTop after the jump, but the user will still see a distracting flicker.
Chris Coyier found a great workaround last year but it’s not meant for every case.

A different solution

Turns out we can take advantage of the History API to do that quite easily. It’s just one line of code:

history.pushState(null, null, '#myhash');

and we can combine it with the old method of setting location.hash to cater for older browsers as well:

if(history.pushState) {
    history.pushState(null, null, '#myhash');
}
else {
    location.hash = '#myhash';
}

Browser support?

The History API is supported by:

  • Firefox 4+
  • Safari 5+
  • Chrome 8+
  • Coming soon in Opera

Enjoy :)

  • http://www.nahueljose.com.ar Nahuel

    Great! :) I’ve been looking for something like this before, good thing I can have it now :P

  • Adam Taylor

    Nice. Unfortunately, what this doesn’t do is update the page’s CSS pseudo-selectors, so changes to :target selectors don’t seem to fire…

    I’ve been using :target to perform a JS-free hide and show of content, with a close button that links to an empty hash (‘#’). As you’d expect, this causes a page jump too, which I’d prefer not to have, and had hoped your solution would fix… Ah, well.

    (Did that make sense? :) )

    • http://leaverou.me Lea Verou

      Aw, that’s so sad :( I guess it won’t fire the hashchange event either :( 

      Thanks for the tip! 

    • DekuLink

      I know this post is 3 years old, but I figured I’d post the solution for anyone that comes across this. Surprisingly, the solution involves no javascript. The HTML for each tab looks like this:

      Tab content goes here

      The CSS:
      .tab {display:none;}
      .target {display:block;top:0;left:0;position:fixed;}
      .target:target + div {display:block !important;}

      The span will stay at the top of the viewbox, so when it scrolls to it, it won’t go anywhere. The + operator selects the div that imediately follows the :target-ed span with the “target” class.

      Hope I helped a few people! This solution was tough to find!

      • DekuLink

        Hmmm… Seems that the html didn’t work. It’s supposed to show a span right before the div. It looks like this: span class=”target” id=”tab1″

        I guess the messed it up. :/

  • http://pulse.yahoo.com/_RCDZENVSDKLPPWFRKJ6ML4NFJM Armindo

    If the history API is not supported:
    | var x = pageXOffset, y = pageYOffset;
    | location.hash = ‘#foo';
    | scrollTo(x,y);
    Have fun

    • http://leaverou.me Lea Verou

      What if the hash is changing through anchor links and not through code?

  • Anonymous

    Anchor Tag with class=’JumpToTop’ href=’#id-name’ and a java script$(‘.JumpToTop’).click(function() { var elementClicked = $(this).attr(“href”);  var destination = $(elementClicked).offset().top; $(“html:not(:animated),body:not(:animated)”).animate({ scrollTop: destination-20}, 1000 ); return false;});  
    will do a better work! what say?

  • http://twitter.com/theMOLITOR Chris Molitor

    Thanks for this! I had no idea about the History API. Really helps on AJAX projects.

  • http://b2bweb.fr/ Julien

    Hello,
    Just to say, it seem to work without the hash for location.hash = ‘myhash';

  • Pingback: How can I update window.location.hash without jumping the document? - Javascript Solution - Developers Q & A

  • Tobias Buschor

    a tricky alternative without flickering:

    var el = document.getElementById(‘myhash’);
    var id = el.id;
    el.removeAttribute(‘id’);
    location.hash = ‘myhash';
    el.setAttribute(‘id’,id);

    • http://www.surreality-rp.com/ ack!

      a hero to the people you are, sir.

    • firiz

      Thanks ;) This is exactly what I want.

  • felix

    Thanks!!

  • http://demain-comme-jamais.toile-libre.org/ vb078

    hhaaaa I would like the same on my futur Website ::
    http://terminatorstudies.org/disnovation/#ICT
    You see, you scroll, the #is-automaticly-added
    :)