jQuery Scrolling Anchors

This is a comprehensive jQuery solution to the Scrolling Anchors effect.  Typical solutions replace the browser’s native “jump-to” behaviour for local anchors with a smooth scrolling effect, but fail to replace all other functionality related to local anchors.  This solution:

  • Adds the link to the history like the browser does natively
  • Respects event bubbling like the browser does natively
  • Handles reload, resubmit, reclick, and back/forward the way the browser does natively
  • Supports external anchors like the browser does natively
  • Additionally supports a class-based include/exclude structure to turn on/off the effect

Ideally, implementing this solution globally across a site should seamlessly add a scrolling anchor effect to all local anchors (technically called fragments, basically a link that contains a hash (#) target), without losing any of the browser’s other native functionality.

This code requires jQuery and the hashchange plugin.  Installing the code turns on scrolling anchors by default.  To turn the effect off, add the class “noscroll” to the target element, and to turn it on, add the class “scrollto”.


jQuery(document).ready(function($)
{
  // Scrolling anchors: Uses jQuery + hashchange
  function scrollto ( $this )
  {
    if ( ! $this.hash ) return ( "" );
    var $this_hash = $($this.hash);
    if ( ! $this_hash ) return ( "" );
    var $parent1 = $this_hash.closest ( '.noscroll' );
    if ( $parent1.length )
    {
      var $parent2 = $this_hash.closest ( '.scrollto' );
      if ( $parent2.length &&
           $parent2.html().length <= $parent1.html().length )
        $parent1 = "";
    }
    if ( ! $parent1.length &&
         ( location.hostname == $this.hostname || ! $this.hostname ) &&
         location.pathname.replace(/^\//,'') == $this.pathname.replace(/^\//,'') )
      return ( $this_hash );
    else
      return ( "" );
  }

  $(window).bind ( 'hashchange', function(e)
  {
    var $this = location, $this_hash = scrollto ( location );
    if ( $this_hash.length )
    {
      var duration = 1000;    // duration in ms
      var easing = 'swing';   // easing values: swing | linear

      $('html,body').animate( { scrollTop: $this_hash.offset().top },
                              duration, easing );
    }
  });

  if ( ! $(window).scrollTop() ) $(window).trigger ( 'hashchange' );

  $('a[href^="#"]').click ( function(e)
  {
    var $this = this, $this_hash = scrollto ( this );
    // Determine applicable cases:
    if ( ! e.isDefaultPrevented() && $this_hash.length )
    {
      e.preventDefault();

      if ( location.hash == $this.hash )
      {
        // Reclick case is special:
        $(window).trigger ( 'hashchange' );
      }
      else
      {
        // Update history without scrolling:
        var hash = $this.hash.substr(1),
            dummy = $( '' ).css({
                                  visibility: 'hidden',
                                  position: 'fixed',
                                  top: '0px'
                                })
                           .attr ( 'id', hash )
                           .prependTo ( document.body );
        $this_hash.attr ( 'id', '' );
        location.hash = $this.hash;
        dummy.remove();
        $this_hash.attr ( 'id', hash );
      }
    }
  });
});

6 thoughts on “jQuery Scrolling Anchors

  1. Hi,

    I’m very new to web development/javascript.
    I’m trying to build a website where links to anchors in external pages would bring you first to the top of the webpage, and then would automatically scroll down to the hash tag anchor down the page.

    (i.e. a link embedded in home.html, to about.html#scrollhere, would bring you to the top of about.html and scroll down to #scrollhere)

    Is this possible using your solution outlined above?
    Is there a place I can see a demo of this scrolling-to-external-anchor functionality?

    Any info would be great-

    1. Hi Krista,

      Yes, it should do what you want.

      This solution was tested with links to anchors on other pages within the site and does work with scrolling. However, the behaviour is intended to emulate the browser’s default behaviour which varies slightly between browsers but is roughly as you described, except in cases where a scroll location is cached by the browser from a previous visit to the page. So for example, if you scroll (manually or otherwise) down a page, then go to another page, then go back to the 1st page, the browser may go back directly to the previous scroll location rather than to the top of the page. In all other cases tested, it does what you want.

      The following link goes to an external web site that has the Scrolling Anchors working, so you can see it in action: http://www.back2front.ca/.

  2. This does not work on Firefox for me, only safari and Chrome… Do you have a version that works on firefox too?

    1. Just a reminder, as noted above, this code:
      “Handles reload, resubmit, reclick, and back/forward the way the browser does natively”
      This differs slightly among browsers, so YMMV. If your browser normally first goes to the top of the page and then jumps down (eg, IE, Chrome), then this code should go to the top of the page, and then scroll down. If the browser goes directly to the anchor normally (eg, FF), then this code should not affect that.

      To test the basic functionality, go to the top of this page, and click on any of the anchors. This works correctly on all browsers tested, including FF (versions 5-13).

Leave a Reply to Zainab Cancel reply

Your email address will not be published. Required fields are marked *