Change the hash of the URL on the scroll button and go back

On the layout of one page with a fixed top menu and navigation binding, I have "scrollspy" that changes the identifier of the fragment on the scroll, gives the menu link to the active class depending on the scroll position and animates the scroll to the anchor using Velocity.js.

Unfortunately, he also does, when I click the back button of the browser, all the scroll steps skip me, that is, I load the site and scroll up and down a small bit, and then often click the back button, the browser also scroll down and up , but don’t go to either the last id visited or the previous one in your browser history.

Here is the jsfiddle.

// jQuery on DOM ready

// In-Page Scroll Animation with VelocityJS
// ------------------------------------------------ //
// https://john-dugan.com/fixed-headers-with-hash-links/
$('.menu-a').on('click', function(e) {
    var hash  = this.hash,
        $hash = $(hash)

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

      $hash.velocity("scroll", { duration: 700, easing: [ .4, .21, .35, 1 ], complete: addHash });

    e.preventDefault();
});

// ScrollSpy for Menu items and Fragment Identifier
// ------------------------------------------------ //
// https://jsfiddle.net/mekwall/up4nu/
$menuLink           = $('.menu-a')

var lastId,
    // Anchors corresponding to menu items
    scrollItems = $menuLink.map(function(){
    var item = $($(this).attr("href"));
        if (item.length) { return item; }
    });


$(window).scroll(function(){
    // Get container scroll position
    var fromTop = $(this).scrollTop()+ 30; // or the value for the #navigation height

    // Get id of current scroll item
    var cur = scrollItems.map(function(){
        if ($(this).offset().top < fromTop)
        return this;
    });

    // Get the id of the current element
    cur = cur[cur.length-1];
    var id = cur && cur.length ? cur[0].id : "";
    if (lastId !== id) {
        lastId = id;

        // Set/remove active class
        $menuLink
        .parent().removeClass("active")
        .end().filter("[href='#"+id+"']").parent().addClass("active");
    }

    // If supported by the browser we can also update the URL
    // http://codepen.io/grayghostvisuals/pen/EtdwL
    if (window.history && window.history.pushState) {
        history.pushState("", document.title,'#'+id);
    }   
});

Using the above code, the following is done:

  • VelocityJS .

  • .

  • , .


1:. , "" , , "" , , .

, , . a) /, , ) (i), (ii), (i).

2: history.pushState IE8, window.location.hash = $(this).attr('id'); . , , window.location.hash = $(this).attr('id'); . HistoryJS - , .

, , , , , "" .

, , , .

:
popState Firefox -
jQuery
document.location.hash

Back Button -

+4
2

, , history.replaceState() history.pushState(). pushState URL- , replaceState URL- , .

, pushState replaceState MDN.

+2

https://github.com/devote/HTML5-History-API, ( ).

:
- , -
- URL, ,
- , , , URL-. , , , .
- Velocity.js , "" ""
- , , , URL- , , ,

, , , . , , , . .

// In-Page Scroll Animation to Menu Link on click
// ------------------------------------------------ //
// https://john-dugan.com/fixed-headers-with-hash-links/
// https://stackoverflow.com/questions/8355673/jquery-how-to-scroll-an-anchor-to-the-top-of-the-page-when-clicked
// http://stackoverflow.com/a/8579673/1010918
// $('a[href^="#"]').on('click', function(e) {
$('.menu-a').on('click', function(e) {

    // default = make hash appear right after click
    // not default = make hash appear after scrolling animation is finished
    e.preventDefault();

    var hash  = this.hash,
        $hash = $(hash)

    $hash.velocity("scroll", { duration: 700, easing: [ .4, .21, .35, 1 ], queue: false });
});



// In-Page Scroll Animation to Hash on Load and Reload
// ----------------------------------------------------------- //
// https://stackoverflow.com/questions/680785/on-window-location-hash-change
// hashchange triggers popstate !
$(window).on('load', function(e) {

    var hash  = window.location.hash;
    console.log('hash on window load '+hash);
    $hash = $(hash)

    $hash.velocity("scroll", { duration: 500, easing: [ .4, .21, .35, 1 ], queue: false });

    // if not URL hash is present = root, go to top of page on reload
    if (!window.location.hash){
        $('body').velocity("scroll", { duration: 500, easing: [ .4, .21, .35, 1 ], queue: false });
    }   
});



// In-Page Scroll Animation to Hash on Back or Forward Button
// ---------------------------------------------------------- //
$('.menu-a').on('click', function(e) {  
    e.preventDefault(); 
    // keep the link in the browser history
    // set this separately from scrolling animation so it works in IE8
    history.pushState(null, null, this.href);
    return false
}); 
$(window).on('popstate', function(e) {
    // alert('popstate fired');
    $('body').append('<div class="console1">popstate fired</div>');
    $('.console1').delay(1000).fadeOut('slow');

    if (!window.location.hash){
        $('body').append('<div class="console2">no window location hash present</div>');

        $('body').velocity("scroll", { duration: 700, easing: [ .4, .21, .35, 1 ], queue: false });

        $('.console2').delay(1000).fadeOut('slow');
    }

    console.log('window.location.hash = '+window.location.hash);
    var hash  = window.location.hash;
    $hash = $(hash)

    $hash.velocity("scroll", { duration: 700, easing: [ .4, .21, .35, 1 ], queue: false });
});



// ScrollSpy for Menu items only - gives selected Menu items the active class
// ------------------------------------------------------------------------ //
// Does not update fragment identifier in URL https://en.wikipedia.org/wiki/Fragment_identifier
// https://jsfiddle.net/mekwall/up4nu/
    var lastId,

    // Anchors corresponding to menu items
    scrollItems = $menuLink.map(function(){
        var item = $($(this).attr("href"));
        // console.table(item);
        if (item.length) { return item; }
    });

    // Give menu item the active class on load of corresponding item
    function scrollSpy () {

        // Get container scroll position
        var fromTop = $(this).scrollTop()+ $menuButtonHeight;

        // Get id of current scroll item
        var cur = scrollItems.map(function(){
            if ($(this).offset().top < fromTop)
            return this;
        });

        // Get the id of the current element
        cur = cur[cur.length - 1];
        var id = cur && cur.length ? cur[0].id : "";

        if (lastId !== id) {
            lastId = id;
            // Set/remove active class
            $menuLink
            .parent().removeClass("active").end()
            .filter("[href='#"+id+"']").parent().addClass("active");
        }

        // Active Menu Link href Attribute
        activeMenuLinkHref = $('.menu-li.active > .menu-a').attr('href');
        // console.log('activeMenuLinkHref '+activeMenuLinkHref);   
    }
    scrollSpy()

    $(window).scroll(function(e){
        scrollSpy()
    });



// On Stop of Scrolling get Active Menu Link Href and set window.location.hash
// --------------------------------------------------------------------------- //

// Scroll Stop Function
//---------------------//
// https://stackoverflow.com/questions/8931605/fire-event-after-scrollling-scrollbars-or-mousewheel-with-javascript
// http://stackoverflow.com/a/8931685/1010918
// http://jsfiddle.net/fbSbT/1/
// http://jsfiddle.net/fbSbT/87/

(function(){ 
    var special = jQuery.event.special,
        uid1 = 'D' + (+new Date()),
        uid2 = 'D' + (+new Date() + 1); 
    special.scrollstart = {
        setup: function() { 
            var timer,
                handler =  function(evt) { 
                    var _self = this,
                        _args = arguments; 
                    if (timer) {
                        clearTimeout(timer);
                    } else {
                        evt.type = 'scrollstart';
                        // throws "TypeError: jQuery.event.handle is undefined"
                        // jQuery.event.handle.apply(_self, _args);
                        // solution
                        // http://stackoverflow.com/a/20809936/1010918
                        // replace jQuery.event.handle.apply with jQuery.event.dispatch.apply
                        jQuery.event.dispatch.apply(_self, _args);
                    } 
                    timer = setTimeout( function(){
                        timer = null;
                    }, special.scrollstop.latency); 
                }; 
            jQuery(this).bind('scroll', handler).data(uid1, handler); 
        },
        teardown: function(){
            jQuery(this).unbind( 'scroll', jQuery(this).data(uid1) );
        }
    }; 
    special.scrollstop = {
        latency: 250,
        setup: function() { 
            var timer,
                    handler = function(evt) { 
                    var _self = this,
                        _args = arguments; 
                    if (timer) {
                        clearTimeout(timer);
                    }
                     timer = setTimeout( function(){ 
                        timer = null;
                        evt.type = 'scrollstop';                        
                        // throws "TypeError: jQuery.event.handle is undefined"
                        // jQuery.event.handle.apply(_self, _args);
                        // solution
                        // http://stackoverflow.com/a/20809936/1010918
                        // replace jQuery.event.handle.apply with jQuery.event.dispatch.apply
                        jQuery.event.dispatch.apply(_self, _args); 
                    }, special.scrollstop.latency); 
                }; 
            jQuery(this).bind('scroll', handler).data(uid2, handler); 
        },
        teardown: function() {
            jQuery(this).unbind( 'scroll', jQuery(this).data(uid2) );
        }
    };

})();



// Scroll Stop Function Called
//----------------------------//

$(window).on("scrollstop", function() {

    // window.history.pushState(null, null, activeMenuLinkHref);
    // window.history.replaceState(null, null, activeMenuLinkHref);

    // http://stackoverflow.com/a/1489802/1010918 //
    // works best really
    hash = activeMenuLinkHref.replace( /^#/, '' );
    console.log('hash '+hash);
    var node = $( '#' + hash );
    if ( node.length ) {
      node.attr( 'id', '' );
      // console.log('node.attr id'+node.attr( 'id', '' ));
    }
    document.location.hash = hash;
    if ( node.length ) {
      node.attr( 'id', hash );
    }
});

CSS

.console1{
    position: fixed;
    z-index: 9999;
    top:0;
    right:0;    
    background-color: #fff;
    border: 2px solid red;
}

.console2{
    position: fixed;
    z-index: 9999;
    bottom:0;
    right:0;    
    background-color: #fff;
    border: 2px solid red;
}

jsfiddle .;)

+1

All Articles