Prevent automatic browser scroll on refresh - javascript

If you go to a page a and scroll around then refresh the page will refresh at the spot where you left it. This is great, however this also occurs on pages where there is a anchor location in the url. An example would be if you clicked on a link http://example.com/post/244#comment5 and refreshed the page after looking around you would not be at the anchor and the page jumps around. Is there any way to prevent this with javascript? So that no-matter-what you would always navigate to the anchor.

On Chrome, even if you force scrollTop to 0 it will jump afterwards after the first scroll event.
You should bind the scroll to this:
$(window).on('beforeunload', function() {
$(window).scrollTop(0);
});
So the browser is tricked to believe that it was on the beginning before the refresh.

To disable automatic scroll restoration just add this tag to head section.
<script>history.scrollRestoration = "manual"</script>
Supported by all modern browsers

After number of failures finally I managed to do the trick. anzo is correct here as using beforeunload will make the page jump to top when a user reloads the page or clicks a link. So unload is the clearly way to do this.
$(window).on('unload', function() {
$(window).scrollTop(0);
});
Javascript way(Thanks ProfNandaa):
window.onunload = function(){ window.scrollTo(0,0); }
EDIT: 16/07/2015
The jump issue is still there with Firefox even with unload event.

This solution is no longer recommended due to changes in browser behavior. See other answers.
Basically, if an anchor is used we bind to the windows scroll event. The idea being that the first scroll event has to belong to the automatic repositioning done by the browser. When this occurs we do our own repositioning and then remove the bound event. This prevents subsequent page scrolls from borking the system.
$(document).ready(function() {
if (window.location.hash) {
//bind to scroll function
$(document).scroll( function() {
var hash = window.location.hash
var hashName = hash.substring(1, hash.length);
var element;
//if element has this id then scroll to it
if ($(hash).length != 0) {
element = $(hash);
}
//catch cases of links that use anchor name
else if ($('a[name="' + hashName + '"]').length != 0)
{
//just use the first one in case there are multiples
element = $('a[name="' + hashName + '"]:first');
}
//if we have a target then go to it
if (element != undefined) {
window.scrollTo(0, element.position().top);
}
//unbind the scroll event
$(document).unbind("scroll");
});
}
});

This works for me.
//Reset scroll top
history.scrollRestoration = "manual"
$(window).on('beforeunload', function(){
$(window).scrollTop(0);
});

Here's a a more general approach. Instead of trying to prevent the browser from scrolling (or jumping to the top as it would look like) I just restore the previous position on the page.
I.e. I'm recording the current y-offset of the page in localStorage and scroll to this position once the page has loaded.
function storePagePosition() {
var page_y = window.pageYOffset;
localStorage.setItem("page_y", page_y);
}
window.addEventListener("scroll", storePagePosition);
var currentPageY;
try {
currentPageY = localStorage.getItem("page_y");
if (currentPageY === undefined) {
localStorage.setItem("page_y") = 0;
}
window.scrollTo( 0, currentPageY );
} catch (e) {
// no localStorage available
}

You can just put a # at the end so the page will load at the top.
Works on all browsers, mobile and desktop, because it is so simple.
$(document).ready(function() {
var url = window.location.href;
console.log(url);
if( url.indexOf('#') < 0 ) {
window.location.replace(url + "#");
} else {
window.location.replace(url);
}
});
// This loads the page with a # at the end.

this works absolutely fine. Nice and clean javascript
var objDiv = document.getElementById("chatbox");
if ( window.history.replaceState ) {
objDiv.scrollTop = objDiv.scrollHeight;
window.history.replaceState( null, null, window.location.href );
}

You should be able to.
Onload, check if window.location.hash has a value. If it does, grab the element with an id that matches the hash value. Find the position of the element (recursive calls to offsetTop/offsetLeft) and then pass those values into the window.scrollTo(x, y) method.
This should scroll the page to the desired element.

Related

Firefox: Anchor and Focus Input Field based on Starting URL

There are similar threads here discussing the fact that Firefox doesn't focus anchored elements properly, and they suggest some kind of timeout-focus etc.
focus() doesn't work with anchor link
I start from a Page URL e.g.
http://myapp.com/page#elementID
Firefox anchors elementID on page load correctly (scrolls into view), but it doesn't auto-focus elementID. I need to also focus the field.
When I add this in my jQuery Document.Ready,
$(document).ready(function() {
// Handle the possible #-Anchor
// Firefox does not focus the anchored element (Chrome does).
// Firefox workaround: If #-Anchor detected in URL, focus this element manually w/timeout
if (window.location.href.indexOf("#") != -1) {
var elementID = window.location.href.split("#")[1];
setTimeout(function() {
document.getElementById(elementID).focus();
}, 10);
}
}
then the Focus starts working, but the Anchor scroll breaks i.e. the page isn't scrolled correctly to the anchor, it gets stuck a little high.
How do I get both Anchor and Focus to work inside the Document Ready for FF?
The .focus() seems to be breaking the hash scroll to, which needs time to acheive.
A longer delay, but short enought for the user not noticing it, will do.
By the way, you could use:
if (window.location.hash != "") {
setTimeout(function() {
$(window.location.hash).focus();
}, 500);
}
Instead of:
if (window.location.href.indexOf("#") != -1) {
var elementID = window.location.href.split("#")[1];
setTimeout(function() {
document.getElementById(elementID).focus();
}, 10);
}
But that is more about code "cosmetic"...
;)

Html anchor linking and hashbang, simple solution?

I have in http://www.g3eo.com/#!/page_About the following in line 96:
<li>Side scan sonar surveys</li>
and need to create an anchor to go to line 180:
<li id="sidescan"><strong>Side scan sonar surveys</strong></li>
I understand that to get this working I would need to do:
<li>Side scan sonar surveys</li>
<li id="sidescan"><a name="sss"><strong>Side scan sonar surveys</strong></a></li>
But this does nothing. I was wondering if the problem is the hashbang in #!/page_Services, without it the web page stops working properly.
Something like this will work:
// Run the code on page load. Change this to whatever your page callback is
window.addEventListener('load', function(e)
{
// Find any of the anchors that have a hash link.
// Change document to whatever the container is for your new elements
as = document.querySelectorAll('a[href^="#"]');
as.forEach(function(a)
{
a.addEventListener('click', function(e)
{
// This stops the hash being added to the URL on click
e.preventDefault();
// Find the hash and the target element (based on ID)
var hash = e.target.href.split('#')[1];
var targetEl = document.getElementById(hash);
// Scroll the window to the target elements offsetTop
window.scrollTo(0, targetEl.offsetTop);
});
});
});
But you'll need to run this code after the content that you want to use is loaded (rather than on page load).
Basically, this simulates hash linking without adding the hash to the url. See here for a working version - https://plnkr.co/edit/mubdlfjuFTgLeYq6ZpCR?p=preview
I started working on a solution very similar to #Liam Egan's, which is good, but I thought "What if someone wants to share a link to an anchor tag? I'll just try using both a hashbang and an anchor hash in the URL!".
After multiple tests, as it turns out, it's really hard to maintain, especially if you use an external library which uses the hash. It will break, so I abandoned that idea.
Here is a solution for clicks on links, which I tested on your website:
$(function(){
$('a[href^="#"]').click(function(e){
// Get the hashes in link
var h = this.href.split('#');
// If the first hash is not a hashbang or if there are several hashes
if(h[1].indexOf('!') !== 0 || h.length > 2) {
// Prevent default behavior of the link so it does not break the site
e.preventDefault();
// If the first hash is a hashbang (but there are multiple hashes),
// only include the first one in the page URL
if(h[1].indexOf('!') === 0) { window.location.hash = '#' + h[1]; }
// Get the element with the right ID (last hash) and its scrolling container
var el = $('#' + h.pop()), cont = el.closest('div[class^="scroll"]');
// Scroll the scrolling container to that element after a delay,
// because it does not work during the page transition
setTimeout(function() {
cont.scrollTop(0) // Reset it first to get the right position below
.scrollTop( el.position().top );
},500);
}
});
});
I had to adapt it for two reasons:
Not the whole document should scroll, just your wrapping .scroll div
The scrolling won't work during page transition, so it needs a delay
It does not affect links such as #!/page_XXX, and will work with links such as #myID or #!/page_XXX#myID.
Finally, for simplicity, since you are using jQuery, I did too. Place that piece of code anywhere on your page after loading jQuery, and it should work.

Prevent browser scroll to hash on document ready, then animate slowly

I have a problem with my script.
It was working perfectly until last week, when my client talked about it.
My website has some links with an hash added to scroll smoothly to id when page is loaded. Now it doesn't scroll smoothly anymore. I did check my variables, and it gets the hash has id (e.g. #content) and also the height of the header nav.
I can't find the problem.
Here's the script:
if (window.location.hash) {
//bind to scroll function
$(document).scroll( function() {
var hash = window.location.hash;
//var hashName = hash.substring(1, hash.length);
var element;
//if element has this id then scroll to it
if ($(hash).length !== 0) {
element = $(hash);
}
//if we have a target then go to it
if (element !== undefined) {
window.scrollTo(0);
}
//unbind the scroll event
$(document).unbind("scroll");
$("html, body").animate({scrollTop: ($(element).offset().top - $('header nav ul').height()) }, 500);
});
}
Thanks in advance
Native scrollers dont work nice. Handling different browsers and mobile devices is realy difficult. Use iScroll my friend http://cubiq.org/iscroll-5 .. it works like a charm and comes with a lot of features!!!

Window scroll for incoming hash path url

I have a page with some fixed and absolute elements that are causing an issue for hash path links. I was able to fix it while the user is navigating around the page with function() { window.scrollBy(0, -80) }; however if I try to call this on my document ready (to scroll for incoming hashes) it does not work.
I found the reason it does not work is that the page does not actually adjust to the hash until after the document ready. If I cannot do this at document ready when can I do it?
As clearly the browser only scrolls to the HTML element from hash only after the whole page is loaded - including all JS -, I think the best option is to bind actions to the scroll event of the page.
You could try something like this:
<script type="text/javascript">
$(function() {
// Retrieves the hash from URL
var hash = window.location.hash.substring(1);
// If hash length > 0 (there is actually a hash)
// And the #hash element exists on page
if(hash.length > 0 && $('#'+ hash).size() > 0){
// Binds a function to the page scroll
$(document).on('scroll', function(){
// Here's the bright part: when the browser finish loading the page, it will
// scroll right to the #hash element. So, whenever the page is scrolled, if
// the #hash element offset top matches the page scroll offset, it means the page
// was scrolled right to that element.
var elemTop = $('#'+ hash).offset().top; // Retrieve element's offset top
var docTop = $(document).scrollTop(); // Retrieve page's offset
if(elemTop == docTop){
alert('Now I do my stuff!! :)');
// Do your stuff
}
// And now you make sure "your stuff" won't happen again if the user
// accidentally scrolls precisely to the #hash element's offset by
// unbinding scroll action of the page.
$(document).unbind('scroll');
});
}
});
</script>
I hope that help you to solve your problem! Let me know if anything was unclear.

distinguish user-initiated scrolling from the browser-initiated scrolling that happens on page load

I'd like to know when the user first does some scrolling on my page, as distinct from the browser-initiated scrolling that happens automatically when you reload a page.
If I try capturing the window's initial scroll position, and then registering an onscroll handler to tell me the scroll position has changed, I don't get too far: browser-initiated scrolling happens after document ready (jQuery's definition), so window.pageYOffset is always 0 on doc ready, even if the browser's right about to jump me down a hundred pixels.
If I try inspecting the onscroll event, nothing seems to let me distinguish a user-initiated event object from a browser-initiated one. The two events have pretty identical properties.
I'm looking for something a little more robust than what's suggested here: How to distinguish scrolling by mouse from scrolling programmatically in JavaScript?.
Thanks...
It's ugly, but possibly the best way to tell is by timing. I notice that on Chrome, the change in pageYOffset happens within 1 millisecond of the window.onload, while on Firefox, it happens within one millisecond of the dom loading. (haven't tested IE, but it is likely that one or the other works) For instance I added this to the bottom of a page:
<script>
window.onload = function () {
var first = window.pageYOffset;
setTimeout(function () {
second = window.pageYOffset;
alert("first: " + first + ", second:" + second)
}, 1);
};
</script>
One chrome, "first" is 0, "second" is a big number, when refreshing a scrolled page. On firefox, that is only true if the below is added right before the closing body tag (which should be the same as jquery's document.ready).
<script>
var first = window.pageYOffset;
setTimeout(function () {
second = window.pageYOffset;
alert("first: " + first + ", second:" + second)
}, 1);
</script>
I think you could detect a "browser refresh initiated" scroll reliably using this sort of technique, but obviously, it's not what I'd call a pretty solution and it might break on a future browser version.
I use a similar technique for a scroll behavior on our home page, not the most elegant but it works well:
<html>
<head></head>
<body>
<div style="height:500px;line-height:500px">
<a id="bm" href="#bm">bookmarked</a>
</div>
<script>
(function(){
var scrollingCanStart = false;
//the user interact with the page
window.onmouseover = window.onkeydown = function(e){
scrollingCanStart = true;
};
//onscroll action
window.onscroll = function(e){
if(!scrollingCanStart){
return;
}
console.log('manual scroll');
};
})();
</script>
</body>
</html>
i was solving the same task and would like to share my approach.
my assumptions:
the user's scroll always preсeded with events like 'wheel',
'another user's scroll' and etc
all events have timeStamp
I have chosen rxjs library for implementation
// this stream will contain last User's scroll
var last_scroll = new Rx.Subject();
var start = rx.Observable.merge(
Rx.Observable.fromEvent(element, 'wheel'),
last_scroll
)
.map(function (e) { return e.timeStamp; });
Rx.Observable.fromEvent(element, 'scroll')
.withLatestFrom(start, function (scroll, start) {
return {scroll: scroll, start: start};
})
.filter(function (o) {
return Math.abs(o.scroll.timeStamp - o.start) <= 500;
})
.map(function (o) {
return o.scroll;
})
.do(function (ev) {
last_scroll.onNext(ev);
})
.subscribe(function (ev) {
// here we have scroll event initiated by user
});
Please feel free to improve this code and share your thoughts. Happy coding!

Categories

Resources