81

Is it possible to remove the hash from window.location without causing the page to jump-scroll to the top? I need to be able to modify the hash without causing any jumps.

I have this:

$('<a href="#123">').text('link').click(function(e) {
  e.preventDefault();
  window.location.hash = this.hash;
}).appendTo('body');

$('<a href="#">').text('unlink').click(function(e) {
  e.preventDefault();
  window.location.hash = '';
}).appendTo('body');

See live example here: http://jsbin.com/asobi

When the user clicks 'link' the hash tag is modified without any page jumps, so that's working fine.

But when the user clicks 'unlink' the has tag is removed and the page scroll-jumps to the top. I need to remove the hash without this side-effect.

Andy E
  • 338,112
  • 86
  • 474
  • 445
David Hellsing
  • 106,495
  • 44
  • 176
  • 212
  • Let me guess: your client thinks the hash is ugly and is polluting the nice and otherwise clean URL that reads: http:// www.example.com/ (without the whitespace)? – anddoutoi Feb 19 '10 at 11:26
  • Good guess, but no. I have bookmarkable modal windows based on hash urls, and when the user closes the window, the hash needs to go. – David Hellsing Feb 19 '10 at 11:30
  • 1
    I am aware that this question is almost 2 years old, but this comment may help a future reader. If you really want to remove the hash, you can change the url using history.pushState(). https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history#Example – ambiguousmouse Jan 02 '12 at 10:22
  • @joshli, sadly it won't work on `IE9`. – Tsukimoto Mitsumasa Sep 19 '12 at 20:09

8 Answers8

98

I believe if you just put in a dummy hash it won't scroll as there is no match to scroll to.

<a href="#A4J2S9F7">No jumping</a>

or

<a href="#_">No jumping</a>

"#" by itself is equivalent to "_top" thus causes a scroll to the top of the page

BenMorel
  • 34,448
  • 50
  • 182
  • 322
scunliffe
  • 62,582
  • 25
  • 126
  • 161
38

I use the following on a few sites, NO PAGE JUMPS!

Nice clean address bar for HTML5 friendly browsers, and just a # for older browsers.

$('#logo a').click(function(e){
    window.location.hash = ''; // for older browsers, leaves a # behind
    history.pushState('', document.title, window.location.pathname); // nice and clean
    e.preventDefault(); // no page reload
});
designosis
  • 5,182
  • 1
  • 38
  • 57
  • 13
    `history.replaceState` might be preferable, since pressing back will then go back to the last *valid* URL. – orlade Dec 15 '12 at 11:47
  • Also, it should be noted that this code is setting making two changes to the navigation history. The result is that when the user presses the back button, they'll be routed first through [url]# and then it will require a second click in order to land on the [url] w/o the #. If you're developing the back button experience, consider a conditional approach so as not to set the state twice. – Vinney Kelly Feb 28 '14 at 18:10
18

window.location's hash property is stupid in a couple of ways. This is one of them; the other is that is has different get and set values:

window.location.hash = "hello";  // url now reads *.com#hello
alert(window.location.hash);   // shows "#hello", which is NOT what I set.
window.location.hash = window.location.hash; // url now reads *.com##hello

Note that setting the hash property to '' removes the hash mark too; that's what redirects the page. To set the value of the hash part of the url to '', leaving the hash mark and therefore not refreshing, write this:

window.location.href = window.location.href.replace(/#.*$/, '#');

There is no way to completely remove the hash mark once set without refreshing the page.

UPDATE 2012:

As Blazemonger and thinkdj have pointed out, technology has improved. Some browsers do allow you to clear that hashtag, but some do not. To support both, try something like:

if ( window.history && window.history.pushState ) { 
    window.history.pushState('', '', window.location.pathname) 
} else { 
    window.location.href = window.location.href.replace(/#.*$/, '#'); 
}
olooney
  • 2,467
  • 1
  • 16
  • 25
3

This is an old post but I wanted to share my solution All the links in my project that being handled by JS are having href="#_js" attribute (or whatever name you want to use for that purposes only), and on page initialization I do:

$('body').on('click.a[href="#_js"]', function() {
    return false;
});

That'll do the trick

Tomer Gal
  • 933
  • 12
  • 21
2

Setting window.location.hash to empty or non-existing anchor name, will always force the page to jump to top. The only way to prevent this is to grab the scroll position of the window and set it to that position again after the hash change.

This will also force a repaint of the page (cant avoid it), though since it's executed in a single js process, it won't jump up/down (theoretically).

$('<a href="#123">').text('link').click(function(e) {
    e.preventDefault();
    window.location.hash = this.hash;
}).appendTo('body');

$('<a href="#">').text('unlink').click(function(e) {
    e.preventDefault();
    var pos = $(window).scrollTop(); // get scroll position
    window.location.hash = '';
    $(window).scrollTop(pos); // set scroll position back
}).appendTo('body');

Hope this helps.

BGerrissen
  • 21
  • 1
  • 1
    not entirely correct... setting the hash to "" scrolls to the top, but if there is no named anchor or element with a matching `id` attribute the browser won't scroll. – scunliffe Feb 19 '10 at 12:00
1

I'm not sure if this produces the desired outcome, give it a shot:

$('<a href="#">').text('unlink').click(function(e) {
    e.preventDefault();
    var st = parseInt($(window).scrollTop())
    window.location.hash = '';
    $('html,body').css( { scrollTop: st });  
});

Basically save the scroll offset and restore it after assigning the empty hash.

Tom Bartel
  • 2,283
  • 1
  • 15
  • 18
  • 1
    it works for me, except I use `$(window).scrollTop(st)` instead of `.css()` – Thomas Joulin Jun 30 '11 at 07:46
  • Tried this one, since I _need_ to change the hash to an existing #id. Everything works well, but when I try to scroll using the mouse, after changing the hash, the scroll jumps to zero. Any ideas on what could cause this? – lucascaro Jul 07 '11 at 15:21
1

Have you tried return false; in the event handler? jQuery does something special when you do that, similar to, but AFAIK more impactful, than e.preventDefault.

Roman Nurik
  • 29,665
  • 7
  • 84
  • 82
  • 2
    `return false;` in jQuery and in native event handlers in compliant browsers is equivalent to `e.preventDefault(); e.stopPropagation();`. I don't think that stopping propagation will help with this issue. – Jesse Hallett Mar 28 '11 at 21:25
0

Hope this helps

html

<div class="tabs">
  <ul>
    <li><a href="#content1">Tab 1</a></li>
    <li><a href="#content2">Tab 2</a></li>
    <li><a href="#content3">Tab 3</a></li>
  </ul>
</div>
<div class="content content1">
    <p>1. Content goes here</p>
</div>
<div class="content content2">
    <p>2. Content goes here</p>
</div>
<div class="content content3">
    <p>3. Content goes here</p>
</div>

js

function tabs(){
  $(".content").hide();

  if (location.hash !== "") {
    $('.tabs ul li:has(a[href="' + location.hash + '"])').addClass("active");
    var hash = window.location.hash.substr(1);
    var contentClass = "." + hash;
    $(contentClass).fadeIn();
  } else {
    $(".tabs ul li").first().addClass("active");
    $('.tabs').next().css("display", "block");
  }
}
tabs();

$(".tabs ul li").click(function(e) {
  $(".tabs ul li").removeAttr("class");
  $(this).addClass("active");
  $(".content").hide();
  var contentClass = "." + $(this).find("a").attr("href").substr(1);
  $(contentClass).fadeIn();
  window.location.hash = $(this).find("a").attr("href");
  e.preventDefault();
  return false;
});

URL without any hash.
http://output.jsbin.com/tojeja

URL with hashtag that does not jumping to anchor.
http://output.jsbin.com/tojeja#content1

Shak Daniel
  • 217
  • 4
  • 11