13

I'm building a one pager website. Eg. every page (5 in total) is on one big page with the main menu fixed at the top. When you click on a menu link it slides you down to that pages anchor tag and the clicked menu item get a "active" CSS class.

What I'd like to do now is allow the user to scroll themself but still have the menu "active" item and URL hash change as they do.

So my question basically is how do I know when the user has scrolled down to a different page so I can update the menu and URL hash (fragment identifier).

Thanks

lowtechsun
  • 1,915
  • 5
  • 27
  • 55
Owen
  • 760
  • 1
  • 7
  • 25
  • 1
    Best up-to-date way to do this is to use the HTML5 History API. See [this related question](https://stackoverflow.com/questions/1397329/how-to-remove-the-hash-from-window-location-with-javascript-without-page-refresh/5298684#answer-5298684) for more details. – rugk Jun 29 '16 at 01:29

4 Answers4

23

its possible but there are a requirement to your page (for my solution to work):

your page have to be separated in divs(or sections whatever) with unique ids (i hope you dont use anchor <a>'s)

than you can use code like this:

$(document).bind('scroll',function(e){
    $('section').each(function(){
        if (
           $(this).offset().top < window.pageYOffset + 10
//begins before top
        && $(this).offset().top + $(this).height() > window.pageYOffset + 10
//but ends in visible area
//+ 10 allows you to change hash before it hits the top border
        ) {
            window.location.hash = $(this).attr('id');
        }
    });
});

with html like this

<section id="home">
  Home
</section>
<section id="works">
  Works
</section>
<section id="about">
  About
</section>
Valerij
  • 27,090
  • 1
  • 26
  • 42
  • Could you please post the code that would go "window.location.hash" part. I used window.location.hash = $(this).attr('href') , but I get #undefined for each hash. Thanks – fluxus Jul 22 '13 at 16:54
  • @suludi those sections don't have an `href` but `id` – Valerij Jul 23 '13 at 11:25
  • Sorry, I noticed that immediatelly myself. But the thing is that it doesn't allow me to scroll at all. I could not scroll on the website I am working on, but there are too many scripts(scrolling ones), so I thought maybe it is a conflict. So I created a simple clean page with 4 sections, and it still doesn't allow me to scroll. Here is the link, help much appreciated: http://madebym.me/test/scroll-hash – fluxus Jul 24 '13 at 20:17
  • +1 And if you want to change the hash on scroll, but don't want to save the hashes in the user's history, I found this works: `var urlId = '#' + $(this).attr('id'); history.replaceState(null, null, urlId);` – Meg Oct 20 '15 at 15:50
  • How about scrolling back? It works well when scrolling down, it changes the hash when the `h1` hit the top (I change section to h1), but when I scroll back up the hash doesn't change to previous id when I pass the `h1` – Andrew.Wolphoe Feb 17 '18 at 08:18
4

I have same issue of this for fix height section content which will change Hash according to the scroll.Somehow this code doesn't work on other browser apart of Chrome. And also manipulate DOM in the scroll then it has to take very long for that event to get process refer http://www.smashingmagazine.com/2013/06/10/pinterest-paint-performance-case-study/ Here are sample code of How I solve this

 (function () {
    // Find all  top,bottom and Hash of each sections,
    // Do this only if the section height remain the same always
    // Move this into the step() if your section height does change.
    // e.g. browser resize
    //
    var section = $.map($("section"), function (e) {
        var $e = $(e);
        var pos = $e.position();
        return {
            top: pos.top,
            bottom: pos.top + $e.height(),
            hash: $e.attr('id')
        };
    });

    //Checking scroll 
    var top = null;
    var changed = false;
    var currentHash = null;

    $(window).scroll(function () {
        var newTop = $(document).scrollTop();

        changed = newTop != top;
        if (changed) {
            top = newTop;
        }

    });

    // You wouldn't want to keep checking the scroll state as
    // it affects the browser performance when it's accessing
    // DOM and reduce your FPS (Frame per Seconds) for scrolling
    // 
    function step() {
        if (!changed) {
            // Top did not change
            return setTimeout(step, 200);
        }
        var count = section.length;
        var p;

        while (p = section[--count]) {
            if (p.top >= top || p.bottom <= top) {
                continue;
            }
            if (currentHash == p.hash) {
                break;
            }
            var scrollTop = $(document).scrollTop();
            window.location.hash = currentHash = p.hash;
            // prevent browser to scroll
            $(document).scrollTop(scrollTop);
        }
        setTimeout(step, 200);
    }
    setTimeout(step, 200);
})(); 

Demo

shiawuen
  • 65
  • 1
  • 5
ahkeno
  • 113
  • 8
1

You are looking for the .scroll() event handler

Andrew
  • 13,757
  • 13
  • 66
  • 84
1

With jquery you can use the scrollTop method to find the scroll position and then compare it to say the position of elements on the page to work out where on the page they are and update accordingly.

Richard Adnams
  • 3,128
  • 2
  • 22
  • 30