How can I remove the location hash without causing the page to scroll?
Asked Answered
A

8

82

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.

Archenteron answered 19/2, 2010 at 11:18 Comment(4)
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)?Capacitate
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.Archenteron
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(). developer.mozilla.org/en/DOM/…Ataliah
@joshli, sadly it won't work on IE9.Northnorthwest
M
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

Majka answered 19/2, 2010 at 11:30 Comment(4)
Not optimal, but good enough. I used '#/' with the same result.Archenteron
window.location.hash = (new_hash === '') ? '_' : new_hash;Crites
Not happy with this solution, what about:#19728520Punchball
I use window.location.hash = "💩";Dosser
F
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
});
Fibrinolysin answered 18/10, 2011 at 10:39 Comment(2)
history.replaceState might be preferable, since pressing back will then go back to the last valid URL.Cupping
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.Courtney
H
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(/#.*$/, '#'); 
}
Helminth answered 19/2, 2010 at 11:39 Comment(4)
Not true. Browsers that support window.history let you do exactly this.Stagg
@Stagg True that! You can use a history.pushState('', document.title, window.location.pathname);Caruncle
But then again, this comment was made in Feb'10 and browsers began supporting pushstates somewhere around mid of 2011Caruncle
The above code does not preserve the query string. Use window.history.pushState('', '', window.location.pathname + window.location.search);Quickie
C
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

Carmellacarmelle answered 17/8, 2012 at 13:13 Comment(0)
P
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.

Pronoun answered 19/2, 2010 at 11:49 Comment(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.Majka
S
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.

Serration answered 19/2, 2010 at 11:35 Comment(2)
it works for me, except I use $(window).scrollTop(st) instead of .css()Giltzow
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?Emersion
M
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.

Microorganism answered 19/2, 2010 at 11:43 Comment(1)
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.Faydra
B
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

Benz answered 3/5, 2015 at 15:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.