Question

I am wanting to add an offset from the top and smooth scroll to the following function, The functionality is on one button thats fixed and follows the user down the page. This button has to be able to scroll through numerous anchors and then go back to the first one, with an offset ideally of 105px from the top. trawled the net for hours for help and dont have the jquery know how myself to fix this, any help?? Similar example here - http://www.google.com/nexus/7/ (button in bottom right)

<script>
var max = 6;

function goToNext() {
    var hash = String(document.location.hash);
    if (hash && hash.indexOf(/anchor/)) {
        var newh = Number(hash.replace("#anchor",""));
        (newh > max-1) ? newh = 0 : void(null);
        document.location.hash = "#anchor" + String(newh+1); 
    } else {
        document.location.hash = "#anchor1";        
    }
}

</script>
<a href="#" onclick="goToNext();return false;"></a>

<div id="anchor1"></div>
<div id="anchor2"></div>
<div id="anchor3"></div>
<div id="anchor4"></div>
<div id="anchor5"></div>
<div id="anchor6"></div>
Was it helpful?

Solution

You can make it scroll smoothly to the element using animate({scrollTop:value},delay).

$('document').ready(function () {
     //on DOM ready change location.hash to 'anchor1'
     window.location.hash = 'anchor1';
    //GO TO NEXT click event:
    $('a').click(function (e) {
        //preventing the default <a> click (href="#")
        e.preventDefault();
        //get the current hash to determine the current <div> id 
        var hash = window.location.hash,
            //find the (next) immediate following sibling of the current <div>
            $next = $(hash).next('div');
        //check if this next <div> sibling exist
        if ($next.length) {
            var id = $next.attr('id'),
                nextOffsetTop = $next.offset().top;
            //animate scrolling and set the new hash
            $('body, html').animate({scrollTop: nextOffsetTop}, 'slow');
            window.location.hash = id;
        }else{
        //else if the next <div> sibling does not exist move to the first anchor
            var first = '#anchor1';
            $('body, html').animate({scrollTop: $(first).offset().top},'slow');
            window.location.hash = first;
        }

    });
})

See this jsfiddle.


Then comes the flickering. Actually it does not flicker but somewhat jerky, if you look closely into the code above. I am setting the animate(scrollTop) first, then changing the hash window.location.hash = id. Now when the animate starts scrolling and suddenly we are changing the hash it tends to jump directly to the next <div> (this is the default haschange event) but pulled back by the animate() and that causes the scrolling to be jerky.

We cannot just stop the default propagation of the haschange event, there may be a solution to do that but cannot guarantee that it would work on all browsers, each browser has different behaviour when it comes to the haschange event. But thanks to @Andy E solution on that SO post you've provided, we don't need to stop the haschange propagation. We can just simply change the hash first, reset it to last scrollTop() position then animate scrolling at will!

//get the current scrollTop value
var st = $(window).scrollTop();
//change the hash
window.location.hash = id;
//reset the scrollTop
$(window).scrollTop(st);
//animate scrolling
$('body, html').animate({scrollTop: nextOffsetTop}, 'slow');

Check this updated jsfiddle.


Now let's talk about HTML5 History API. The reason I didn't introduced this at first because it is implemented differently across HTML5 (especially IE) browsers and has no fallback for HTML4 browsers, making this method somehow inconsistent. But you can get this done properly using a plugin I guess.

Here's how you can do it using history.pushState():

if ($next.length) {
    var id = $next.attr('id'),
        nextOffsetTop = $next.offset().top;

    history.pushState({state:id}, id, '#'+id);
    $('body, html').animate({scrollTop: nextOffsetTop - 105}, 'slow');      
} 

See this jsfiddle.

That's it. Cheers!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top