Smooth scrolling without using jQuery

I am encoding a page where I want to use only JavaScript code for the UI without any interference from plugins or frameworks.

And now I'm trying my best to scroll through the page without jQuery.

+74
javascript html
Apr 08 2018-12-12T00:
source share
13 answers

Try a smooth scrolling demo or an algorithm like:

  • Get current top location using self.pageYOffset
  • Get the position of an element until you scroll to: element.offsetTop
  • To do this, you need to make a for loop, which will be quite fast, or use a timer to smoothly scroll to this position using window.scrollTo



See also another popular answer to this question.




Andrew Johnson original code:

 function currentYPosition() { // Firefox, Chrome, Opera, Safari if (self.pageYOffset) return self.pageYOffset; // Internet Explorer 6 - standards mode if (document.documentElement && document.documentElement.scrollTop) return document.documentElement.scrollTop; // Internet Explorer 6, 7 and 8 if (document.body.scrollTop) return document.body.scrollTop; return 0; } function elmYPosition(eID) { var elm = document.getElementById(eID); var y = elm.offsetTop; var node = elm; while (node.offsetParent && node.offsetParent != document.body) { node = node.offsetParent; y += node.offsetTop; } return y; } function smoothScroll(eID) { var startY = currentYPosition(); var stopY = elmYPosition(eID); var distance = stopY > startY ? stopY - startY : startY - stopY; if (distance < 100) { scrollTo(0, stopY); return; } var speed = Math.round(distance / 100); if (speed >= 20) speed = 20; var step = Math.round(distance / 25); var leapY = stopY > startY ? startY + step : startY - step; var timer = 0; if (stopY > startY) { for ( var i=startY; i<stopY; i+=step ) { setTimeout("window.scrollTo(0, "+leapY+")", timer * speed); leapY += step; if (leapY > stopY) leapY = stopY; timer++; } return; } for ( var i=startY; i>stopY; i-=step ) { setTimeout("window.scrollTo(0, "+leapY+")", timer * speed); leapY -= step; if (leapY < stopY) leapY = stopY; timer++; } } 



Related links:

+23
Apr 08 2018-12-12T00:
source share

Edit: This answer was written in 2013. Please check Christian Train's comment below about the request.

I have done it. The code below is independent of any structure.

Restriction: The active anchor is not specified in the URL.

Code Version: 1.0 | Github: https://github.com/Yappli/smooth-scroll

 (function() // Code in a function to create an isolate scope { var speed = 500; var moving_frequency = 15; // Affects performance ! var links = document.getElementsByTagName('a'); var href; for(var i=0; i<links.length; i++) { href = (links[i].attributes.href === undefined) ? null : links[i].attributes.href.nodeValue.toString(); if(href !== null && href.length > 1 && href.substr(0, 1) == '#') { links[i].onclick = function() { var element; var href = this.attributes.href.nodeValue.toString(); if(element = document.getElementById(href.substr(1))) { var hop_count = speed/moving_frequency var getScrollTopDocumentAtBegin = getScrollTopDocument(); var gap = (getScrollTopElement(element) - getScrollTopDocumentAtBegin) / hop_count; for(var i = 1; i <= hop_count; i++) { (function() { var hop_top_position = gap*i; setTimeout(function(){ window.scrollTo(0, hop_top_position + getScrollTopDocumentAtBegin); }, moving_frequency*i); })(); } } return false; }; } } var getScrollTopElement = function (e) { var top = 0; while (e.offsetParent != undefined && e.offsetParent != null) { top += e.offsetTop + (e.clientTop != null ? e.clientTop : 0); e = e.offsetParent; } return top; }; var getScrollTopDocument = function() { return document.documentElement.scrollTop + document.body.scrollTop; }; })(); 
+28
Nov 06 '13 at 9:24
source share

The natural scroll of the browser in JavaScript looks like this:

 // scroll to specific values, // same as window.scroll() method. // for scrolling a particular distance, use window.scrollBy(). window.scroll({ top: 2500, left: 0, behavior: 'smooth' }); // scroll certain amounts from current position window.scrollBy({ top: 100, // negative value acceptable left: 0, behavior: 'smooth' }); // scroll to a certain element document.querySelector('.hello').scrollIntoView({ behavior: 'smooth' }); 
+24
Apr 30 '17 at 9:00
source share

Algorithm

Scrolling an element requires changing the scrollTop value over time. Within a given moment, calculate the new scrollTop value. To smoothly animate, interpolate using a smooth step algorithm .

Calculate scrollTop as follows:

 var point = smooth_step(start_time, end_time, now); var scrollTop = Math.round(start_top + (distance * point)); 

Where:

  • start_time - start time of the animation;
  • end_time - this is when the animation ends (start_time + duration) ;
  • start_top - value of scrollTop at the beginning; and
  • distance - the difference between the desired final value and the initial value (target - start_top) .

A sustainable solution should detect when the animation is interrupted, and much more. Read my post on Seamless Scrolling without jQuery for details.

Demo

See JSFiddle .

Implementation

The code:

 /** Smoothly scroll element to the given target (element.scrollTop) for the given duration Returns a promise that fulfilled when done, or rejected if interrupted */ var smooth_scroll_to = function(element, target, duration) { target = Math.round(target); duration = Math.round(duration); if (duration < 0) { return Promise.reject("bad duration"); } if (duration === 0) { element.scrollTop = target; return Promise.resolve(); } var start_time = Date.now(); var end_time = start_time + duration; var start_top = element.scrollTop; var distance = target - start_top; // based on http://en.wikipedia.org/wiki/Smoothstep var smooth_step = function(start, end, point) { if(point <= start) { return 0; } if(point >= end) { return 1; } var x = (point - start) / (end - start); // interpolation return x*x*(3 - 2*x); } return new Promise(function(resolve, reject) { // This is to keep track of where the element scrollTop is // supposed to be, based on what we're doing var previous_top = element.scrollTop; // This is like a think function from a game loop var scroll_frame = function() { if(element.scrollTop != previous_top) { reject("interrupted"); return; } // set the scrollTop for this frame var now = Date.now(); var point = smooth_step(start_time, end_time, now); var frameTop = Math.round(start_top + (distance * point)); element.scrollTop = frameTop; // check if we're done! if(now >= end_time) { resolve(); return; } // If we were supposed to scroll but didn't, then we // probably hit the limit, so consider it done; not // interrupted. if(element.scrollTop === previous_top && element.scrollTop !== frameTop) { resolve(); return; } previous_top = element.scrollTop; // schedule next frame for execution setTimeout(scroll_frame, 0); } // boostrap the animation process setTimeout(scroll_frame, 0); }); } 
+22
Aug 29 '14 at 18:38
source share

I gave an example without jQuery here: http://codepen.io/sorinnn/pen/ovzdq

 /** by Nemes Ioan Sorin - not an jQuery big fan therefore this script is for those who love the old clean coding style @id = the id of the element who need to bring into view Note : this demo scrolls about 12.700 pixels from Link1 to Link3 */ (function() { window.setTimeout = window.setTimeout; // })(); var smoothScr = { iterr : 30, // set timeout miliseconds ..decreased with 1ms for each iteration tm : null, //timeout local variable stopShow: function() { clearTimeout(this.tm); // stopp the timeout this.iterr = 30; // reset milisec iterator to original value }, getRealTop : function (el) // helper function instead of jQuery { var elm = el; var realTop = 0; do { realTop += elm.offsetTop; elm = elm.offsetParent; } while(elm); return realTop; }, getPageScroll : function() // helper function instead of jQuery { var pgYoff = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop; return pgYoff; }, anim : function (id) // the main func { this.stopShow(); // for click on another button or link var eOff, pOff, tOff, scrVal, pos, dir, step; eOff = document.getElementById(id).offsetTop; // element offsetTop tOff = this.getRealTop(document.getElementById(id).parentNode); // terminus point pOff = this.getPageScroll(); // page offsetTop if (pOff === null || isNaN(pOff) || pOff === 'undefined') pOff = 0; scrVal = eOff - pOff; // actual scroll value; if (scrVal > tOff) { pos = (eOff - tOff - pOff); dir = 1; } if (scrVal < tOff) { pos = (pOff + tOff) - eOff; dir = -1; } if(scrVal !== tOff) { step = ~~((pos / 4) +1) * dir; if(this.iterr > 1) this.iterr -= 1; else this.itter = 0; // decrease the timeout timer value but not below 0 window.scrollBy(0, step); this.tm = window.setTimeout(function() { smoothScr.anim(id); }, this.iterr); } if(scrVal === tOff) { this.stopShow(); // reset function values return; } } } 
+6
May 20 '14 at 21:19
source share

Modern browsers support the CSS property of the "scroll behavior: smooth". Therefore, we do not even need Javascript for this. Just add this to the body element and use regular anchors and links. Scrolling MDNs

+6
May 09 '18 at 13:48
source share

I recently decided to solve this problem in a situation where jQuery was not an option, so I register my solution here only for posterity.

 var scroll = (function() { var elementPosition = function(a) { return function() { return a.getBoundingClientRect().top; }; }; var scrolling = function( elementID ) { var el = document.getElementById( elementID ), elPos = elementPosition( el ), duration = 400, increment = Math.round( Math.abs( elPos() )/40 ), time = Math.round( duration/increment ), prev = 0, E; function scroller() { E = elPos(); if (E === prev) { return; } else { prev = E; } increment = (E > -20 && E < 20) ? ((E > - 5 && E < 5) ? 1 : 5) : increment; if (E > 1 || E < -1) { if (E < 0) { window.scrollBy( 0,-increment ); } else { window.scrollBy( 0,increment ); } setTimeout(scroller, time); } else { el.scrollTo( 0,0 ); } } scroller(); }; return { To: scrolling } })(); /* usage */ scroll.To('elementID'); 

The scroll() function uses the module’s scroll.To('id') template to pass the identifier of the target element to its scrolling() function via scroll.To('id') , which sets the values ​​to be used using the scroller() function.

Structure

In scrolling() :

  • el : DOM target
  • elPos : returns a function via elememtPosition() , which with each call gives the position of the target element relative to the top of the page.
  • duration : transition time in milliseconds.
  • increment : divides the starting position of the target element into 40 steps.
  • time : sets the time of each step.
  • prev : the previous position of the target element in scroller() .
  • E : holds the position of the target element in scroller() .

The actual work is done using the scroller() function, which continues to call itself (via setTimeout() ) until the target element is at the top of the page or the page can scroll more.

Each time scroller() is called, it checks the current position of the target element (held in the E variable), and if it is > 1 OR < -1 , and if the page still scrolls, shifts the window by increment pixels - up or down, if E is a positive or negative value. If E is neither > 1 OR < -1 , or E === prev , the function stops. I added the DOMElement.scrollTo() method at completion to make sure the target element was hacked at the top of the window (and not that you would notice it on a part of the pixel!).

The if on line 2 of scroller() checks if the page scrolls (in cases where the target can be at the bottom of the page and the page cannot scroll further) by checking E against its previous position ( prev ).

The triple condition below it reduces the value of increment , as E approaches zero. This stops the page overflowing in one direction, and then bounces back to re-adjust the other, and then bounces back to flip another, ping-pong style, to infinity and beyond.

If your page is larger than c.4000px, you can increase the values ​​in the first term expression (here by +/- 20) and / or the divider, which sets the value of increment (here to 40).

When playing with duration , a divisor that sets increment , and triple-state scroller() should allow you to customize the function to fit your page.

  • Jsfiddle

  • NB Tested in modern versions of Firefox and Chrome on Lubuntu, as well as Firefox, Chrome and IE on Windows8.

+5
Oct 21 '15 at 18:08
source share

I did something like this. I do not know if it works in IE8. Tested in IE9, Mozilla, Chrome, Edge.

 function scroll(toElement, speed) { var windowObject = window; var windowPos = windowObject.pageYOffset; var pointer = toElement.getAttribute('href').slice(1); var elem = document.getElementById(pointer); var elemOffset = elem.offsetTop; var counter = setInterval(function() { windowPos; if (windowPos > elemOffset) { // from bottom to top windowObject.scrollTo(0, windowPos); windowPos -= speed; if (windowPos <= elemOffset) { // scrolling until elemOffset is higher than scrollbar position, cancel interval and set scrollbar to element position clearInterval(counter); windowObject.scrollTo(0, elemOffset); } } else { // from top to bottom windowObject.scrollTo(0, windowPos); windowPos += speed; if (windowPos >= elemOffset) { // scroll until scrollbar is lower than element, cancel interval and set scrollbar to element position clearInterval(counter); windowObject.scrollTo(0, elemOffset); } } }, 1); } //call example var navPointer = document.getElementsByClassName('nav__anchor'); for (i = 0; i < navPointer.length; i++) { navPointer[i].addEventListener('click', function(e) { scroll(this, 18); e.preventDefault(); }); } 

Description

  • pointer -get element and chceck if it has the attribute "href", if "yes", get rid of "#"
  • elem variable without "#"
  • elemOffset -offset the scroll to element at the top of the page
+2
Oct. 16 '16 at 10:10
source share

You can use

 document.querySelector('your-element').scrollIntoView({behavior: 'smooth'}); 

If you want to scroll to the top of the page, you can simply place an empty element at the top and smooth the scroll to it.

+1
Jan 31 '18 at 19:32
source share

You can also use the Scroll Behavior property. for example, add the line below to your CSS

 html{ scroll-behavior:smooth; } 

and this will result in a built-in smooth scrolling function. More on scroll behavior

+1
Aug 16 '19 at 11:20
source share
 <script> var set = 0; function animatescroll(x, y) { if (set == 0) { var val72 = 0; var val73 = 0; var setin = 0; set = 1; var interval = setInterval(function() { if (setin == 0) { val72++; val73 += x / 1000; if (val72 == 1000) { val73 = 0; interval = clearInterval(interval); } document.getElementById(y).scrollTop = val73; } }, 1); } } </script> 

x = scrollTop
y = id div, which is used to scroll

Note. To make the body scroll, give the body an identifier.

0
Sep 01 '17 at 7:54 on
source share

You can use the for loop with windows.scrollTo and setTimeout windows to scroll with simple Javascript. To scroll an element with scrollToSmoothly function: scrollToSmoothly(elem.offsetTop) (assuming elem is a DOM element). You can use this to seamlessly jump to any y position in the document.

 function scrollToSmoothly(pos, time){ /*Time is only applicable for scrolling upwards*/ /*Code written by hev1*/ /*pos is the y-position to scroll to (in pixels)*/ if(isNaN(pos)){ throw "Position must be a number"; } if(pos<0){ throw "Position can not be negative"; } var currentPos = window.scrollY||window.screenTop; if(currentPos<pos){ var t = 10; for(let i = currentPos; i <= pos; i+=10){ t+=10; setTimeout(function(){ window.scrollTo(0, i); }, t/2); } } else { time = time || 2; var i = currentPos; var x; x = setInterval(function(){ window.scrollTo(0, i); i -= 10; if(i<=pos){ clearInterval(x); } }, time); } } 

Demo version:

 <button onClick="scrollToDiv()">Scroll To Element</button> <div style="margin: 1000px 0px; text-align: center;">Div element<p/> <button onClick="scrollToSmoothly(Number(0))">Scroll back to top</button> </div> <script> function scrollToSmoothly(pos, time){ /*Time is only applicable for scrolling upwards*/ /*Code written by hev1*/ /*pos is the y-position to scroll to (in pixels)*/ if(isNaN(pos)){ throw "Position must be a number"; } if(pos<0){ throw "Position can not be negative"; } var currentPos = window.scrollY||window.screenTop; if(currentPos<pos){ var t = 10; for(let i = currentPos; i <= pos; i+=10){ t+=10; setTimeout(function(){ window.scrollTo(0, i); }, t/2); } } else { time = time || 2; var i = currentPos; var x; x = setInterval(function(){ window.scrollTo(0, i); i -= 10; if(i<=pos){ clearInterval(x); } }, time); } } function scrollToDiv(){ var elem = document.querySelector("div"); scrollToSmoothly(elem.offsetTop); } </script> 
0
Aug 04 '18 at 20:37
source share

Here is my solution. Works in most browsers

 document.getElementById("scrollHere").scrollIntoView({behavior: "smooth"}); 

Documents

 document.getElementById("end").scrollIntoView({behavior: "smooth"}); 
 body {margin: 0px; display: block; height: 100%; background-image: linear-gradient(red, yellow);} .start {display: block; margin: 100px 10px 1000px 0px;} .end {display: block; margin: 0px 0px 100px 0px;} 
 <div class="start">Start</div> <div class="end" id="end">End</div> 
0
Jun 20 '19 at 14:11
source share



All Articles