120

What I'm trying to do is make it so that if you click on a button, it scrolls down (smoothly) to a specific div on the page.

What I need is if you click on the button, it smooth scrolls to the div 'second'.

.first {
    width: 100%;
    height: 1000px;
    background: #ccc;
}

.second {
    width: 100%;
    height: 1000px;
    background: #999;
}
<div class="first"><button type="button">Click Me!</button></div>
<div class="second">Hi</div>
Rob
  • 26,989
  • 16
  • 82
  • 98
Erik Fischer
  • 1,461
  • 3
  • 13
  • 16
  • 1
    this should help you:: http://stackoverflow.com/questions/3432656/scroll-to-a-div-using-jquery – Sudhir Bastakoti Aug 06 '13 at 02:59
  • You could try [this plugin](https://github.com/kswedberg/jquery-smooth-scroll) Although, [this one](http://www.dwuser.com/education/content/quick-guide-adding-smooth-scrolling-to-your-webpages/) is far more user friendly. You pretty much just have to link the JS file in the header, and change your markup appropriately for it to work. – joshrathke Aug 06 '13 at 02:55

8 Answers8

203

do:

$("button").click(function() {
    $('html,body').animate({
        scrollTop: $(".second").offset().top},
        'slow');
});

Updated Jsfiddle

Sudhir Bastakoti
  • 99,167
  • 15
  • 158
  • 162
55

There are many examples of smooth scrolling using JS libraries like jQuery, Mootools, Prototype, etc.

The following example is on pure JavaScript. If you have no jQuery/Mootools/Prototype on page or you don't want to overload page with heavy JS libraries the example will be of help.

http://jsfiddle.net/rjSfP/

HTML Part:

<div class="first"><button type="button" onclick="smoothScroll(document.getElementById('second'))">Click Me!</button></div>
<div class="second" id="second">Hi</div>

CSS Part:

.first {
    width: 100%;
    height: 1000px;
    background: #ccc;
}

.second {
    width: 100%;
    height: 1000px;
    background: #999;
}

JS Part:

window.smoothScroll = function(target) {
    var scrollContainer = target;
    do { //find scroll container
        scrollContainer = scrollContainer.parentNode;
        if (!scrollContainer) return;
        scrollContainer.scrollTop += 1;
    } while (scrollContainer.scrollTop == 0);

    var targetY = 0;
    do { //find the top of target relatively to the container
        if (target == scrollContainer) break;
        targetY += target.offsetTop;
    } while (target = target.offsetParent);

    scroll = function(c, a, b, i) {
        i++; if (i > 30) return;
        c.scrollTop = a + (b - a) / 30 * i;
        setTimeout(function(){ scroll(c, a, b, i); }, 20);
    }
    // start scrolling
    scroll(scrollContainer, scrollContainer.scrollTop, targetY, 0);
}
nico
  • 837
  • 5
  • 4
33

What if u use scrollIntoView function?

var elmntToView = document.getElementById("sectionId");
elmntToView.scrollIntoView(); 

Has {behavior: "smooth"} too.... ;) https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

Ionut B.
  • 351
  • 3
  • 6
12

I played around with nico's answer a little and it felt jumpy. Did a bit of investigation and found window.requestAnimationFrame which is a function that is called on each repaint cycle. This allows for a more clean-looking animation. Still trying to hone in on good default values for step size but for my example things look pretty good using this implementation.

var smoothScroll = function(elementId) {
    var MIN_PIXELS_PER_STEP = 16;
    var MAX_SCROLL_STEPS = 30;
    var target = document.getElementById(elementId);
    var scrollContainer = target;
    do {
        scrollContainer = scrollContainer.parentNode;
        if (!scrollContainer) return;
        scrollContainer.scrollTop += 1;
    } while (scrollContainer.scrollTop == 0);

    var targetY = 0;
    do {
        if (target == scrollContainer) break;
        targetY += target.offsetTop;
    } while (target = target.offsetParent);

    var pixelsPerStep = Math.max(MIN_PIXELS_PER_STEP,
                                 (targetY - scrollContainer.scrollTop) / MAX_SCROLL_STEPS);

    var stepFunc = function() {
        scrollContainer.scrollTop =
            Math.min(targetY, pixelsPerStep + scrollContainer.scrollTop);

        if (scrollContainer.scrollTop >= targetY) {
            return;
        }

        window.requestAnimationFrame(stepFunc);
    };

    window.requestAnimationFrame(stepFunc);
}
Ned Rockson
  • 1,095
  • 7
  • 16
  • 1
    @Alfonso See above. This is simply an optimized version of nico's code in a previous answer. – Ned Rockson Jan 07 '16 at 16:55
  • @NedRockson Doesn't work for me gives console message "Uncaught TypeError: Cannot read property 'parentNode' of null" but nico's code is working, What should I do so can apply clean animation ? – Kartik Watwani Sep 23 '17 at 05:49
  • @KartikWatwani That means that on the line reading `scrollContainer = scrollContainer.parentNode`, scrollContainer is null. This likely means that you are not passing the correct `elementId` when calling this function. It's also possible that you are running this script on a page where that elementId does not exist. – Ned Rockson Sep 25 '17 at 16:28
  • @NedRockson If `elementId` would have been wrong I would have received the same error in case of @nico 's example but in that case scrolling is working but not smoothly. – Kartik Watwani Sep 27 '17 at 04:22
  • Using `requestAnimationFrame` instead of `setTimeout` is the way to go. `setTimeout` shouldn't be use for animations. – tsnkff Dec 18 '17 at 14:55
  • This solution is awesome and has performances in mind, thank you! Also, consider `window.smoothScroll = …` at the beginning instead of `var smoothScroll = …`. – vcoppolecchia Mar 26 '19 at 16:49
  • For anyone running into the "parentNode of null" error as mentioned by Kartik Watwani, just be aware that for Ned's solution you just pass the element's ID to the smoothScroll function, whereas with nico's solution you pass the element itself. – paddyfields Oct 29 '19 at 13:52
  • Can confirm this works in 2022 – jeff Jun 15 '22 at 22:04
7

The solution that worked for me:

var element = document.getElementById("box");

element.scrollIntoView({behavior: "smooth"});

You can explore more options here

Jyoti Duhan
  • 988
  • 1
  • 16
  • 26
6

You can use basic css to achieve smooth scroll

html {
  scroll-behavior: smooth;
}
2

I took the Ned Rockson's version and adjusted it to allow upwards scrolls as well.

var smoothScroll = function(elementId) {
  var MIN_PIXELS_PER_STEP = 16;
  var MAX_SCROLL_STEPS = 30;
  var target = document.getElementById(elementId);
  var scrollContainer = target;
  do {
    scrollContainer = scrollContainer.parentNode;
    if (!scrollContainer) return;
    scrollContainer.scrollTop += 1;
  } while (scrollContainer.scrollTop === 0);

  var targetY = 0;
  do {
    if (target === scrollContainer) break;
    targetY += target.offsetTop;
  } while (target = target.offsetParent);

  var pixelsPerStep = Math.max(MIN_PIXELS_PER_STEP,
    Math.abs(targetY - scrollContainer.scrollTop) / MAX_SCROLL_STEPS);

  var isUp = targetY < scrollContainer.scrollTop;

  var stepFunc = function() {
    if (isUp) {
      scrollContainer.scrollTop = Math.max(targetY, scrollContainer.scrollTop - pixelsPerStep);
      if (scrollContainer.scrollTop <= targetY) {
        return;
      }
    } else {
        scrollContainer.scrollTop = Math.min(targetY, scrollContainer.scrollTop + pixelsPerStep);

      if (scrollContainer.scrollTop >= targetY) {
        return;
      }
    }

    window.requestAnimationFrame(stepFunc);
  };

  window.requestAnimationFrame(stepFunc);
};
Marek Lisý
  • 3,302
  • 2
  • 20
  • 28
1

Ned Rockson basically answers this question. However there is a fatal flaw within his solution. When the targeted element is closer to the bottom of the page than the viewport-height, the function doesn't reach its exit statement and traps the user on the bottom of the page. This is simply solved by limiting the iteration count.

var smoothScroll = function(elementId) {
    var MIN_PIXELS_PER_STEP = 16;
    var MAX_SCROLL_STEPS = 30;
    var target = document.getElementById(elementId);
    var scrollContainer = target;
    do {
        scrollContainer = scrollContainer.parentNode;
        if (!scrollContainer) return;
        scrollContainer.scrollTop += 1;
    } while (scrollContainer.scrollTop == 0);

    var targetY = 0;
    do {
        if (target == scrollContainer) break;
        targetY += target.offsetTop;
    } while (target = target.offsetParent);

    var pixelsPerStep = Math.max(MIN_PIXELS_PER_STEP,
                                 (targetY - scrollContainer.scrollTop) / MAX_SCROLL_STEPS);

    var iterations = 0;
    var stepFunc = function() {
        if(iterations > MAX_SCROLL_STEPS){
            return;
        }
        scrollContainer.scrollTop =
            Math.min(targetY, pixelsPerStep + scrollContainer.scrollTop);

        if (scrollContainer.scrollTop >= targetY) {
            return;
        }

        window.requestAnimationFrame(stepFunc);
    };

    window.requestAnimationFrame(stepFunc);
}
  • Does the if statement with the following comparison, iterations > MAX_SCROLL_STEPS, even occur? The comparison is always returns false as iterations is initially set to 0 and never changes. Similarly, MAX_SCROLL_STEPS never changes as well. – jeff Jun 29 '22 at 22:12