9

I have implemented the CSS Tricks Smooth Page Scroll on my site and it's working pretty nicely. However, because I have a fixed nav at the top of the page, when the page scrolls to the appropriate anchor div, the top of the div disappears behind the nav. How can I offset the scroll (about 70px) so that the whole div is shown? I tried doing this:

var targetOffset = $target.offset().top - 70;

But that doesn't quite work. The page scrolls to the appropriate spot but then it immediately jumps back up so that the top of the div is hidden. What am I missing? Here's the code in full:

$(function() {

    function filterPath(string) {
        return string
        .replace(/^\//,'')
        .replace(/(index|default).[a-zA-Z]{3,4}$/,'')
        .replace(/\/$/,'');
    }

    var locationPath = filterPath(location.pathname);
    var scrollElem = scrollableElement('html', 'body');

    // Any links with hash tags in them (can't do ^= because of fully qualified URL potential)
    $('a[href*=#]').each(function() {

        // Ensure it's a same-page link
        var thisPath = filterPath(this.pathname) || locationPath;
        if (  locationPath == thisPath
            && (location.hostname == this.hostname || !this.hostname)
            && this.hash.replace(/#/,'') ) {

                // Ensure target exists
                var $target = $(this.hash), target = this.hash;
                if (target) {

                    // Find location of target
                    var targetOffset = $target.offset().top - 70;
                    $(this).click(function(event) {

                        // Prevent jump-down
                        event.preventDefault();

                        // Animate to target
                        $(scrollElem).animate({scrollTop: targetOffset}, 400, function() {

                            // Set hash in URL after animation successful
                            location.hash = target;

                        });
                    });
                }
        }

    });


    // Use the first element that is "scrollable"  (cross-browser fix?)
    function scrollableElement(els) {
        for (var i = 0, argLength = arguments.length; i <argLength; i++) {
            var el = arguments[i],
            $scrollElement = $(el);
            if ($scrollElement.scrollTop()> 0) {
                return el;
            } else {
                $scrollElement.scrollTop(1);
                var isScrollable = $scrollElement.scrollTop()> 0;
                $scrollElement.scrollTop(0);
                if (isScrollable) {
                    return el;
                }
            }
        }
        return [];
    }

});

Thanks in advance for your help.

mcography
  • 1,081
  • 6
  • 19
  • 36
  • I found this [link](http://css-tricks.com/forums/discussion/17304/adding-margin-top-to-scroll-js/p1) that helped me understand that the reason why it was jumping was because it was resetting the URL after the animation. So now I have a new question, is there any way to reset the URL and get rid of the jumping? I'd like the URL to reflect where the user is at on the page. – mcography Nov 08 '12 at 17:18

5 Answers5

15

This always happens. I search and search for an answer, get frustrated, post a question asking for help, and then immediately find an answer to my problem. Silly. Anyway, here's the solution for anyone who might be having the same problem.

If you want to change the offset by 70px, for example, change the code to this:

var targetOffset = $target.offset().top - 70;

However, unless you remove this line from the code...

location.hash = target;

... the page will scroll to the right spot and then immediately jump back up so that the top of the div is hidden behind the header. You can remove the above line from the code and everything will work great, except for the fact that the URL will no longer change to reflect the user's position on the page.

If you want the URL to change (and I think this is a good idea for usability purposes), then all you have to do is change the CSS for the anchor divs. Add a positive value for padding-top and a negative value for margin-top. For example:

#anchor-name {
padding-top: 70px;
margin-top: -70px;
}

I only have 3 divs, so I just plugged in that CSS to each of them and voila, everything worked. However, if you have a lot of anchor divs, you might consider creating a class of .anchor, putting the CSS there, and applying the class to all the appropriate divs.

I hope this helps!

mcography
  • 1,081
  • 6
  • 19
  • 36
  • Oh, and if you're using Twitter Bootstrap's scrollspy, don't forget to add `data-offset="70"` to the place in your code where you are calling the scrollspy feature. It should look something like this: ` >`. – mcography Nov 08 '12 at 18:31
9

I have fixed such a kind of issue with below code:

Working demo HERE. You can play with "Post Topics" section in the sidebar and content in the main-content area.

Code

jQuery(function() {
  jQuery('a[href*=#]:not([href=#])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {

      var target = jQuery(this.hash);
      target = target.length ? target : jQuery('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        jQuery('html,body').animate({
          scrollTop: target.offset().top -100
        }, 1000);
        return false;
      }
    }
  });
});
Kushal Jayswal
  • 925
  • 1
  • 12
  • 31
  • in the case whereby you run into the error ```Uncaught Error: Syntax error, unrecognized expression: a[href*=#]:not([href=#])```. just escape the `#` like this ``a[href*=\\#]:not([href=\\#])'`` – amaugo somto Dec 05 '19 at 15:36
3

CSS scroll margin or padding

There's no longer a need for JavaScript.

/* Scroll to 2rem above the target */
:target {
  scroll-margin-top: 2rem;
}

/* Bonus for a smooth scrolling experience */
body {
  scroll-behavior: smooth;
}

Read more about scroll margin and scroll padding on MDN.

Merchako
  • 789
  • 1
  • 8
  • 19
1

Refer to https://codepen.io/pikeshmn/pen/mMxEdZ

Approach: We get the height of fixed nav using document.getElementById('header').offsetHeight And offset the scroll to this value.

var jump=function(e){  

e.preventDefault();                        //prevent "hard" jump
  var target = $(this).attr("href");       //get the target

      //perform animated scrolling
      $('html,body').animate(
        {
          scrollTop: $(target).offset().top - document.getElementById('header').offsetHeight - 5  //get top-position of target-element and set it as scroll target
        },1000,function()                  //scrolldelay: 1 seconds
        {
          location.hash = target;          //attach the hash (#jumptarget) to the pageurl
        });
      }

  $(document).ready(function()
  {
    $('a[href*="#"]').bind("click", jump); //get all hrefs
    return false;
  });
Pikesh Prasoon
  • 377
  • 2
  • 8
0

Actually there's a CSS rule for that : scroll-padding-top :) Combined with a regular padding-top for the top-most element of course.

cf. https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-padding-top

Mazerty
  • 51
  • 1
  • 4