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

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;
}
});
});

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

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);

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.

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

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.

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.

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');

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.

When using laravel framework, I had some issues with using a route->back() function since it erased my hash. In order to keep my hash, I created a simple function:
$(function() {
if (localStorage.getItem("hash") ){
location.hash = localStorage.getItem("hash");
}
});
and I set it in my other JS function like this:
localStorage.setItem("hash", myvalue);
You can name your local storage values any way you like; mine named hash.
Therefore, if the hash is set on PAGE1 and then you navigate to PAGE2; the hash will be recreated on PAGE1 when you click Back on PAGE2.

Related

animated scrolling script prevents local anchor positions to be accessed from external links

I have a onepager site where I use scrollmagic plus all its necessary plugins/libraries (and jQuery) for different effects where animation, pinning, fading processes etc. are triggered by scroll positions.
I also use it for animated scrolling to the anchor points on the page (from the menu and from other local links) - see the according part of the script below.
The problem is that this script suppresses the default behaviour of "jumping" directly to an anchorpoint when a local link is clicked, and apparently also when the page is accessed from outside via a direct link or bookmark with an anchor appended to the URL (like http://www.example.com/index.php#part3). Altough this behaviour is desired when clicking a local link, it obviously prevents the browser from displaying the anchor position when an anchor is linked from somewhere else.
Is there any way to make the browser directly display that anchor position when a link like in the above example is clicked?
var sm_controller_1 = new ScrollMagic.Controller();
sm_controller_1.scrollTo(function(anchor_id) {
TweenMax.to(window, 2.0, {
scrollTo: {
y: anchor_id
autoKill: true
},
ease: Cubic.easeInOut
});
});
jQuery(document).on("click", "a[href^=#]", function(e) {
var id = jQuery(this).attr('href');
if(jQuery(id).length > 0) {
e.preventDefault();
sm_controller_1.scrollTo(id);
if (window.history && window.history.pushState) {
history.pushState("", document.title, id);
}
}
});
(It doesn't make sense to create a fiddle/codepen since the problem lies in calling the original URL from an external source).
Well assuming scroll magic doesnt have extra functionality that is not posted here that would get in the way of my answer you could try this:
Add a data-attribute to your links which you want to use default behavior:
<a href="example.com/index.php#part3.php" data-default="true">
Check if that data attribute exists and if it does return true in your click handler to continue with the default behavior:
var sm_controller_1 = new ScrollMagic.Controller();
sm_controller_1.scrollTo(function(anchor_id) {
TweenMax.to(window, 2.0, {
scrollTo: {
y: anchor_id
autoKill: true
},
ease: Cubic.easeInOut
});
});
jQuery(document).on("click", "a[href^=#]", function(e) {
if(e.currentTarget.dataset.default){
return true;
}
var id = jQuery(this).attr('href');
if(jQuery(id).length > 0) {
e.preventDefault();
sm_controller_1.scrollTo(id);
if (window.history && window.history.pushState) {
history.pushState("", document.title, id);
}
}
});
You can try and use this code:
jQuery(function ($) {
$(document).ready(function() {// if needed, use window.onload which fires after this event
if(window.location.hash) {
var hash = window.location.hash;
$( 'a[href=' + hash + ']' ).click();
}
});
});
It will wait till the DOM (or the page) is loaded and then simulate the user click on the nav item.
After I've read your question one more time, I am not sure anymore if you want your page loaded on the position of the element which is listed in the anchor or the scroll wasn't working when coming from an external source?
If the scroll was working but you wanted to display the page at the right place, like a jump, then I propose 2 solutions:
a) use the CSS opacity:0; on the body and after the scroll is finished, set it back to opacity:1;
b) try to jump on the proper place on the page before you load ScrollMagic

Change the url and keep a div

I saw something really different, and I have no idea how to do it.
The site Rdio.com when you click in any link, the url change totally (not #).
But the div in the bottom of the page (that is playing the song) do not reload.
How they do this?
you can do this with an ajax load and then you mainipulate the browser history.
like so:
/*clickhandler Hauptmenü*/
$('#main-nav a').on('click', function(e){
var href = $(this).attr('href'),
title = $(this).text();
loadContent(href,title);
/*manipulate Browser history */
history.pushState({path: href, titel: title}, $(this).attr('href'), 'http://www.example.com/'+$(this).attr('href'));
e.preventDefault();
});
window.addEventListener('popstate', function(e){
loadContent(e.state.path, e.state.titel);
}, false);
function loadContent(href,title){
var href = href,
container = $('#main-cont');
container.fadeOut(100, function(){
container.load( href +' #main-cont', function(){
container.fadeIn(200);
$('title').replaceWith('<title>' + title + '</title>');
});
});
};
I hope this answers your question.
This is done with JavaScript's new history object, using the pushState and popState methods. See also http://diveintohtml5.info/history.html
Correct me if I'm wrong - considering that I havent been to their site - but I believe that they would be using an Iframe of some sorts - considering that they would have to reload that div if they did otherwise

How do I navigate to hashtag after page load?

I want to do the inverse of what I've been finding so far. I'm setting a lot of heights with js and I want to navigate to the hashtag in the url after the page has loaded. I'm guessing this is simple but I'm not seeing the obvious answer... for an example, check here...
http://alavita.rizenclients.com/#story
Attempted this using the code...
$(window).load(function() {
var hashTag = window.location.hash;
window.location = '/' + hashTag;
});
doesn't actually take me to the top of the tagged section...
If you simply want to change the hash after page loads:
window.onload = function (event) {
window.location.hash = "#my-new-hash";
};
If you want to navigate to the URL with new hash:
window.location.href = "http://website.com/#my-new-hash";
If you want to listen for changes in the hash of the URL; you can consider using the window.onhashchange DOM event.
window.onhashchange = function () {
if (location.hash === "#expected-hash") {
doSomething();
}
};
But it is not supported by every major browser yet. It now has a wide browser support. You can also check for changes by polling the window.location.hash on small intervals, but this is not very efficient either.
For a cross-browser solution; I would suggest Ben Alman's jQuery hashchange plugin that combines these methods and a few others with a fallback mechanism.
EDIT: After your question update, I understand you want the page to scroll to a bookmark?:
You can use Element.scrollTop or jQuery's $.scrollTop() method.
$(document).ready(function (event) {
var yOffset = $("#my-element").offset().top;
$("body").scrollTop(yOffset);
});
See documentation here.
For some reason both MS Edge 42 and IE 11 will not scroll to the new bookmark for me, even when doing a window.location.reload(true) after setting the new bookmark. So I came up with this solution: insert this script on the page you're loading (requires jquery)
$(document).ready(function() {
var hash = window.location.hash;
if (hash) {
var elem = document.getElementById(hash.substring(1));
if (elem) {
elem.scrollIntoView();
}
}
});
Using scrollTo or scrollIntoView will not respect any offset created by the :target css selector, which is often used to make the page scroll to just above the anchor, by setting it to position: relative with a negative top.
This will scroll to the anchor while respecting the :target selector:
if (location.hash) {
window.location.replace(location.hash);
}
You could just set the current location:
window.location = 'http://alavita.rizenclients.com/#story';
Or set the hash (if it isn't already), then reload:
window.location.hash = hashTag;
window.location=window.location.href;
You changed your question.
Check out this solution. https://stackoverflow.com/a/2162174/973860 so you understand what is going on and how to implement a cross browser solution.
NOTICE: At the bottom he mentions a jquery plugin that will do what you need.
http://benalman.com/projects/jquery-hashchange-plugin/
This plugin will allow you to do something like this. This will work for your current page. But you may want to modify it to be more robust.
$(function(){
// Bind the event.
$(window).hashchange( function(){
// get the hash
var hash = window.location.hash;
// click for your animation
$('a[href=' + hash + ']').click();
})
// Trigger the event (useful on page load).
$(window).hashchange();
});

Exception for hashchange function - browser history back?

I have no idea how I should even call this problem … the title of this question doesn't make any sense at all, I know!
Following case: I have a single-page layout where users scroll downwards. I have sections .layer which, when inside the viewport should change the hash in the addressbar to its id. So e.g. the .layer#one is inside the viewport the url in the addressbar looks like this www.whatever.com/#!/one
$(window).scroll(function() {
hash = $('.layer:in-viewport').attr('id');
top.location.hash = "!/" + hash;
});
This works just fine and is exactly like I want it. The reason I have this syntax with the !/ is that if I would simply set the location to hash only the scroll-behaviour would be buggy because the browser tries to stick to the hash position.
The problem is now, that I want to be able to make browser history-back button working!
This would normally be rather simple with the hashchange function that comes with jQuery like so…
$(window).bind( 'hashchange', function( event ) {
//query the hash in the addressbar and jump to its position on the site
});
The only problem I have with this is that the hashchange function would also be triggered if the hash is changed while scrolling. So it would again jump or stick to the current position in the browser. Any idea how I could solve this? I could probably unbind the hashchange while scrolling, right? But is this the best solution?
Sure, you could just unbind and rebind whenever the hash changes on scroll. For example:
var old_hash;
var scroller = function() {
hash = $('.layer:in-viewport').attr('id');
if ( old_hash != hash ) {
$(window).off('hashchange', GoToHash); // using jQuery 1.7+ - change to unbind for < 1.7
top.location.hash = "!/" + hash;
setTimeout(function() { // ensures this happens in the next event loop
$(window).on('hashchange', GoToHash);
}, 0);
old_hash = hash;
}
}
var GoToHash = function() {
//query the hash in the addressbar and jump to its position on the site
}
$(window).scroll(scroller);

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

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.
I believe if you just put in a dummy hash it won't scroll as there is no match to scroll to.
No jumping
or
No jumping
"#" by itself is equivalent to "_top" thus causes a scroll to the top of the page
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
});
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(/#.*$/, '#');
}
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
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.
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.
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.
Hope this helps
html
<div class="tabs">
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</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

Categories

Resources