Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I update window.location.hash without jumping the document?

I have a sliding panel set up on my website.

When it finished animating, I set the hash like so

function() {
   window.location.hash = id;
}

(this is a callback, and the id is assigned earlier).

This works good, to allow the user to bookmark the panel, and also for the non JavaScript version to work.

However, when I update the hash, the browser jumps to the location. I guess this is expected behaviour.

My question is: how can I prevent this? I.e. how can I change the window's hash, but not have the browser scroll to the element if the hash exists? Some sort of event.preventDefault() sort of thing?

I'm using jQuery 1.4 and the scrollTo plugin.

Many thanks!

Update

Here is the code that changes the panel.

$('#something a').click(function(event) {
    event.preventDefault();
    var link = $(this);
    var id = link[0].hash;

    $('#slider').scrollTo(id, 800, {
        onAfter: function() {

            link.parents('li').siblings().removeClass('active');
            link.parent().addClass('active');
            window.location.hash = id;

            }
    });
});
like image 744
alex Avatar asked Oct 06 '10 06:10

alex


People also ask

What does Window location hash do?

The hash property of the Location interface returns a string containing a '#' followed by the fragment identifier of the URL — the ID on the page that the URL is trying to target. The fragment is not percent-decoded. If the URL does not have a fragment identifier, this property contains an empty string, "" .

How do I add a hash to a URL?

The hash of a url can be found by creating a new URL Javascript object from the URL string, and then using its hash property to get the value of the hash fragment. Note that this will include the # character also. If the url does not contains a hash, then an empty string "" will be returned.


8 Answers

There is a workaround by using the history API on modern browsers with fallback on old ones:

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

Credit goes to Lea Verou

like image 89
Attila Fulop Avatar answered Nov 15 '22 03:11

Attila Fulop


The problem is you are setting the window.location.hash to an element's ID attribute. It is the expected behavior for the browser to jump to that element, regardless of whether you "preventDefault()" or not.

One way to get around this is to prefix the hash with an arbitrary value like so:

window.location.hash = 'panel-' + id.replace('#', '');

Then, all you need to do is to check for the prefixed hash on page load. As an added bonus, you can even smooth scroll to it since you are now in control of the hash value...

$(function(){
    var h = window.location.hash.replace('panel-', '');
    if (h) {
        $('#slider').scrollTo(h, 800);
    }
});

If you need this to work at all times (and not just on the initial page load), you can use a function to monitor changes to the hash value and jump to the correct element on-the-fly:

var foundHash;
setInterval(function() {
    var h = window.location.hash.replace('panel-', '');
    if (h && h !== foundHash) {
        $('#slider').scrollTo(h, 800);
        foundHash = h;
    }
}, 100);
like image 31
Derek Hunziker Avatar answered Nov 15 '22 02:11

Derek Hunziker


Cheap and nasty solution.. Use the ugly #! style.

To set it:

window.location.hash = '#!' + id;

To read it:

id = window.location.hash.replace(/^#!/, '');

Since it doesn't match and anchor or id in the page, it won't jump.

like image 28
Gavin Brock Avatar answered Nov 15 '22 03:11

Gavin Brock


I used a combination of Attila Fulop (Lea Verou) solution for modern browsers and Gavin Brock solution for old browsers as follows:

if (history.pushState) {
    // IE10, Firefox, Chrome, etc.
    window.history.pushState(null, null, '#' + id);
} else {
    // IE9, IE8, etc
    window.location.hash = '#!' + id;
}

As observed by Gavin Brock, to capture the id back you will have to treat the string (which in this case can have or not the "!") as follows:

id = window.location.hash.replace(/^#!?/, '');

Before that, I tried a solution similar to the one proposed by user706270, but it did not work well with Internet Explorer: as its Javascript engine is not very fast, you can notice the scroll increase and decrease, which produces a nasty visual effect.

like image 23
aldemarcalazans Avatar answered Nov 15 '22 03:11

aldemarcalazans


Why dont you get the current scroll position, put it in a variable then assign the hash and put the page scroll back to where it was:

var yScroll=document.body.scrollTop;
window.location.hash = id;
document.body.scrollTop=yScroll;

this should work

like image 22
user706270 Avatar answered Nov 15 '22 03:11

user706270


This solution worked for me.

The problem with setting location.hash is that the page will jump to that id if it's found on the page.

The problem with window.history.pushState is that it adds an entry to the history for each tab the user clicks. Then when the user clicks the back button, they go to the previous tab. (this may or may not be what you want. it was not what I wanted).

For me, replaceState was the better option in that it only replaces the current history, so when the user clicks the back button, they go to the previous page.

$('#tab-selector').tabs({
  activate: function(e, ui) {
    window.history.replaceState(null, null, ui.newPanel.selector);
  }
});

Check out the History API docs on MDN.

like image 36
Mike Vallano Avatar answered Nov 15 '22 03:11

Mike Vallano


This solution worked for me

// store the currently selected tab in the hash value
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
$('#myTab a[href="' + hash + '"]').tab('show');

And my full js code is

$('#myTab a').click(function(e) {
  e.preventDefault();
  $(this).tab('show');
});

// store the currently selected tab in the hash value
$("ul.nav-tabs > li > a").on("shown.bs.tab", function(e) {
  var id = $(e.target).attr("href").substr(1);
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }
   // window.location.hash = '#!' + id;
});

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
// console.log(hash);
$('#myTab a[href="' + hash + '"]').tab('show');
like image 35
Rohit Dhiman Avatar answered Nov 15 '22 04:11

Rohit Dhiman


I'm not sure if you can alter the original element but how about switch from using the id attr to something else like data-id? Then just read the value of data-id for your hash value and it won't jump.

like image 21
Jon B Avatar answered Nov 15 '22 03:11

Jon B