I want to have 4 buttons/links on the beginning of the page, and under them the content.
On the buttons I put this code:
Scroll to element 1
Scroll to element 2
Scroll to element 3
Scroll to element 4
And under links there will be content:
<h2 id="idElement1">Element1</h2>
content....
<h2 id="idElement2">Element2</h2>
content....
<h2 id="idElement3">Element3</h2>
content....
<h2 id="idElement4">Element4</h2>
content....
It is working now, but cannot make it look more smooth.
I used this code, but cannot get it to work.
$('html, body').animate({
scrollTop: $("#elementID").offset().top
}, 2000);
Any suggestions? Thank you.
Edit: and the fiddle: http://jsfiddle.net/WxJLx/2/
Super smoothly with requestAnimationFrame
For smoothly rendered scrolling animation one could use window.requestAnimationFrame() which performs better with rendering than regular setTimeout() solutions.
A basic example looks like this. Function step is called for browser's every animation frame and allows for better time management of repaints, and thus increasing performance.
function doScrolling(elementY, duration) {
var startingY = window.pageYOffset;
var diff = elementY - startingY;
var start;
// Bootstrap our animation - it will get called right before next frame shall be rendered.
window.requestAnimationFrame(function step(timestamp) {
if (!start) start = timestamp;
// Elapsed milliseconds since start of scrolling.
var time = timestamp - start;
// Get percent of completion in range [0, 1].
var percent = Math.min(time / duration, 1);
window.scrollTo(0, startingY + diff * percent);
// Proceed with animation as long as we wanted it to.
if (time < duration) {
window.requestAnimationFrame(step);
}
})
}
For element's Y position use functions in other answers or the one in my below-mentioned fiddle.
I set up a bit more sophisticated function with easing support and proper scrolling to bottom-most elements:
https://jsfiddle.net/s61x7c4e/
Question was asked 5 years ago and I was dealing with smooth scroll and felt giving a simple solution is worth it to those who are looking for. All the answers are good but here you go a simple one.
function smoothScroll(){
document.querySelector('.your_class or #id here').scrollIntoView({
behavior: 'smooth'
});
}
just call the smoothScroll function on onClick event on your source element.
DOCS: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
Note: Please check compatibility here
3rd Party edit
Support for Element.scrollIntoView() in 2020 is this:
Region full + partial = sum full+partial Support
Asia 73.24% + 22.75% = 95.98%
North America 56.15% + 42.09% = 98.25%
India 71.01% + 20.13% = 91.14%
Europe 68.58% + 27.76% = 96.35%
Just made this javascript only solution below.
Simple usage:
EPPZScrollTo.scrollVerticalToElementById('signup_form', 20);
Engine object (you can fiddle with filter, fps values):
/**
*
* Created by Borbás Geri on 12/17/13
* Copyright (c) 2013 eppz! development, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
var EPPZScrollTo =
{
/**
* Helpers.
*/
documentVerticalScrollPosition: function()
{
if (self.pageYOffset) return self.pageYOffset; // Firefox, Chrome, Opera, Safari.
if (document.documentElement && document.documentElement.scrollTop) return document.documentElement.scrollTop; // Internet Explorer 6 (standards mode).
if (document.body.scrollTop) return document.body.scrollTop; // Internet Explorer 6, 7 and 8.
return 0; // None of the above.
},
viewportHeight: function()
{ return (document.compatMode === "CSS1Compat") ? document.documentElement.clientHeight : document.body.clientHeight; },
documentHeight: function()
{ return (document.height !== undefined) ? document.height : document.body.offsetHeight; },
documentMaximumScrollPosition: function()
{ return this.documentHeight() - this.viewportHeight(); },
elementVerticalClientPositionById: function(id)
{
var element = document.getElementById(id);
var rectangle = element.getBoundingClientRect();
return rectangle.top;
},
/**
* Animation tick.
*/
scrollVerticalTickToPosition: function(currentPosition, targetPosition)
{
var filter = 0.2;
var fps = 60;
var difference = parseFloat(targetPosition) - parseFloat(currentPosition);
// Snap, then stop if arrived.
var arrived = (Math.abs(difference) <= 0.5);
if (arrived)
{
// Apply target.
scrollTo(0.0, targetPosition);
return;
}
// Filtered position.
currentPosition = (parseFloat(currentPosition) * (1.0 - filter)) + (parseFloat(targetPosition) * filter);
// Apply target.
scrollTo(0.0, Math.round(currentPosition));
// Schedule next tick.
setTimeout("EPPZScrollTo.scrollVerticalTickToPosition("+currentPosition+", "+targetPosition+")", (1000 / fps));
},
/**
* For public use.
*
* #param id The id of the element to scroll to.
* #param padding Top padding to apply above element.
*/
scrollVerticalToElementById: function(id, padding)
{
var element = document.getElementById(id);
if (element == null)
{
console.warn('Cannot find element with id \''+id+'\'.');
return;
}
var targetPosition = this.documentVerticalScrollPosition() + this.elementVerticalClientPositionById(id) - padding;
var currentPosition = this.documentVerticalScrollPosition();
// Clamp.
var maximumScrollPosition = this.documentMaximumScrollPosition();
if (targetPosition > maximumScrollPosition) targetPosition = maximumScrollPosition;
// Start animation.
this.scrollVerticalTickToPosition(currentPosition, targetPosition);
}
};
Smooth scrolling - look ma no jQuery
Based on an article on itnewb.com i made a demo plunk to smoothly scroll without external libraries.
The javascript is quite simple. First a helper function to improve cross browser support to determine the current position.
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;
}
Then a function to determine the position of the destination element - the one where we would like to scroll to.
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;
}
And the core function to do the scrolling
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++;
}
return false;
}
To call it you just do the following. You create a link which points to another element by using the id as a reference for a destination anchor.
<a href="#anchor-2"
onclick="smoothScroll('anchor-2');">smooth scroll to the headline with id anchor-2<a/>
...
... some content
...
<h2 id="anchor-2">Anchor 2</h2>
Copyright
In the footer of itnewb.com the following is written: The techniques, effects and code demonstrated in ITNewb articles may be used for any purpose without attribution (although we recommend it) (2014-01-12)
You could also check this great Blog - with some very simple ways to achieve this :)
https://css-tricks.com/snippets/jquery/smooth-scrolling/
Like (from the blog)
// Scroll to specific values
// scrollTo is the same
window.scroll({
top: 2500,
left: 0,
behavior: 'smooth'
});
// Scroll certain amounts from current position
window.scrollBy({
top: 100, // could be negative value
left: 0,
behavior: 'smooth'
});
// Scroll to a certain element
document.querySelector('.hello').scrollIntoView({
behavior: 'smooth'
});
and you can also get the element "top" position like below (or some other way)
var e = document.getElementById(element);
var top = 0;
do {
top += e.offsetTop;
} while (e = e.offsetParent);
return top;
Why not use CSS scroll-behavior property
html {
scroll-behavior: smooth;
}
The browser support is also good
https://caniuse.com/#feat=css-scroll-behavior
For a more comprehensive list of methods for smooth scrolling, see my answer here.
To scroll to a certain position in an exact amount of time, window.requestAnimationFrame can be put to use, calculating the appropriate current position each time. To scroll to an element, just set the y-position to element.offsetTop.
/*
#param pos: the y-position to scroll to (in pixels)
#param time: the exact amount of time the scrolling will take (in milliseconds)
*/
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
Demo:
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
document.getElementById("toElement").addEventListener("click", function(e){
scrollToSmoothly(document.querySelector('div').offsetTop, 500 /* milliseconds */);
});
document.getElementById("backToTop").addEventListener("click", function(e){
scrollToSmoothly(0, 500);
});
<button id="toElement">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element
<button id="backToTop">Scroll back to top</button>
</div>
The SmoothScroll.js library can also be used, which supports scrolling to an element on the page in addition to more complex features such as smooth scrolling both vertically and horizontally, scrolling inside other container elements, different easing behaviors, scrolling relatively from the current position, and more.
document.getElementById("toElement").addEventListener("click", function(e){
smoothScroll({toElement: document.querySelector('div'), duration: 500});
});
document.getElementById("backToTop").addEventListener("click", function(e){
smoothScroll({yPos: 'start', duration: 500});
});
<script src="https://cdn.jsdelivr.net/gh/LieutenantPeacock/SmoothScroll#1.2.0/src/smoothscroll.min.js" integrity="sha384-UdJHYJK9eDBy7vML0TvJGlCpvrJhCuOPGTc7tHbA+jHEgCgjWpPbmMvmd/2bzdXU" crossorigin="anonymous"></script>
<button id="toElement">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element
<button id="backToTop">Scroll back to top</button>
</div>
Alternatively, you can pass an options object to window.scroll which scrolls to a specific x and y position and window.scrollBy which scrolls a certain amount from the current position:
// Scroll to specific values
// scrollTo is the same
window.scroll({
top: 2500,
left: 0,
behavior: 'smooth'
});
// Scroll certain amounts from current position
window.scrollBy({
top: 100, // could be negative value
left: 0,
behavior: 'smooth'
});
If you only need to scroll to an element, not a specific position in the document, you can use Element.scrollIntoView with behavior set to smooth.
document.getElementById("elemID").scrollIntoView({
behavior: 'smooth'
});
I've been using this for a long time:
function scrollToItem(item) {
var diff=(item.offsetTop-window.scrollY)/8
if (Math.abs(diff)>1) {
window.scrollTo(0, (window.scrollY+diff))
clearTimeout(window._TO)
window._TO=setTimeout(scrollToItem, 30, item)
} else {
window.scrollTo(0, item.offsetTop)
}
}
usage:
scrollToItem(element) where element is document.getElementById('elementid') for example.
Variation of #tominko answer.
A little smoother animation and resolved problem with infinite invoked setTimeout(), when some elements can't allign to top of viewport.
function scrollToItem(item) {
var diff=(item.offsetTop-window.scrollY)/20;
if(!window._lastDiff){
window._lastDiff = 0;
}
console.log('test')
if (Math.abs(diff)>2) {
window.scrollTo(0, (window.scrollY+diff))
clearTimeout(window._TO)
if(diff !== window._lastDiff){
window._lastDiff = diff;
window._TO=setTimeout(scrollToItem, 15, item);
}
} else {
console.timeEnd('test');
window.scrollTo(0, item.offsetTop)
}
}
you can use this plugin. Does exactly what you want.
http://flesler.blogspot.com/2007/10/jqueryscrollto.html
If one need to scroll to an element inside a div there is my solution based on Andrzej Sala's answer:
function scroolTo(element, duration) {
if (!duration) {
duration = 700;
}
if (!element.offsetParent) {
element.scrollTo();
}
var startingTop = element.offsetParent.scrollTop;
var elementTop = element.offsetTop;
var dist = elementTop - startingTop;
var start;
window.requestAnimationFrame(function step(timestamp) {
if (!start)
start = timestamp;
var time = timestamp - start;
var percent = Math.min(time / duration, 1);
element.offsetParent.scrollTo(0, startingTop + dist * percent);
// Proceed with animation as long as we wanted it to.
if (time < duration) {
window.requestAnimationFrame(step);
}
})
}
Why not use this easy way
Native JS
document.querySelector(".layout").scrollIntoView({
behavior: "smooth",
});
Smooth scrolling with jQuery.ScrollTo
To use the jQuery ScrollTo plugin you have to do the following
Create links where href points to another elements.id
create the elements you want to scroll to
reference jQuery and the scrollTo Plugin
Make sure to add a click event handler for each link that should do smooth scrolling
Creating the links
<h1>Smooth Scrolling with the jQuery Plugin .scrollTo</h1>
<div id="nav-list">
Scroll to element 1
Scroll to element 2
Scroll to element 3
Scroll to element 4
</div>
Creating the target elements here only the first two are displayed the other headings are set up the same way. To see another example i added a link back to the navigation a.toNav
<h2 id="idElement1">Element1</h2>
....
<h2 id="idElement1">Element1</h2>
...
<a class="toNav" href="#nav-list">Scroll to Nav-List</a>
Setting the references to the scripts. Your path to the files may be different.
<script src="./jquery-1.8.3.min.js"></script>
<script src="./jquery.scrollTo-1.4.3.1-min.js"></script>
Wiring it all up
The code below is borrowed from jQuery easing plugin
jQuery(function ($) {
$.easing.elasout = function (x, t, b, c, d) {
var s = 1.70158; var p = 0; var a = c;
if (t == 0) return b;
if ((t /= d) == 1) return b + c;
if (!p) p = d * .3;
if (a < Math.abs(c)) {
a = c; var s = p / 4;
} else var s = p / (2 * Math.PI) * Math.asin(c / a);
// line breaks added to avoid scroll bar
return a * Math.pow(2, -10 * t) * Math.sin((t * d - s)
* (2 * Math.PI) / p) + c + b;
};
// important reset all scrollable panes to (0,0)
$('div.pane').scrollTo(0);
$.scrollTo(0); // Reset the screen to (0,0)
// adding a click handler for each link
// within the div with the id nav-list
$('#nav-list a').click(function () {
$.scrollTo(this.hash, 1500, {
easing: 'elasout'
});
return false;
});
// adding a click handler for the link at the bottom
$('a.toNav').click(function () {
var scrollTargetId = this.hash;
$.scrollTo(scrollTargetId, 1500, {
easing: 'elasout'
});
return false;
});
});
Fully working demo on plnkr.co
You may take a look at the soucre code for the demo.
Update May 2014
Based on another question i came across another solution from kadaj. Here jQuery animate is used to scroll to an element inside a <div style=overflow-y: scroll>
$(document).ready(function () {
$('.navSection').on('click', function (e) {
debugger;
var elemId = ""; //eg: #nav2
switch (e.target.id) {
case "nav1":
elemId = "#s1";
break;
case "nav2":
elemId = "#s2";
break;
case "nav3":
elemId = "#s3";
break;
case "nav4":
elemId = "#s4";
break;
}
$('.content').animate({
scrollTop: $(elemId).parent().scrollTop()
+ $(elemId).offset().top
- $(elemId).parent().offset().top
}, {
duration: 1000,
specialEasing: { width: 'linear'
, height: 'easeOutBounce' },
complete: function (e) {
//console.log("animation completed");
}
});
e.preventDefault();
});
});
Related
I want to scroll to the bottom of the div when the page loads. I am using jQuery's animate() function this. The issue is that my code is working on desktop view, but if I change it to mobile view then my code is not working.
$(".chat-msg-list").animate({
scrollTop: $(".chat-msg-list").prop("scrollHeight")
}, 1000);
jQuery:
$(".chat-msg-list").animate({
scrollTop: $(".chat-msg-list").offset().top},
}, 1000);
Javascript:
function getElementY(query) {
return window.pageYOffset + document.querySelector(query).getBoundingClientRect().top
}
function doScrolling(element, duration) {
var startingY = window.pageYOffset
var elementY = getElementY(element)
// If element is close to page's bottom then window will scroll only to some position above the element.
var targetY = document.body.scrollHeight - elementY < window.innerHeight ? document.body.scrollHeight - window.innerHeight : elementY
var diff = targetY - startingY
// Easing function: easeInOutCubic
// From: https://gist.github.com/gre/1650294
var easing = function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
var start
if (!diff) return
// Bootstrap our animation - it will get called right before next frame shall be rendered.
window.requestAnimationFrame(function step(timestamp) {
if (!start) start = timestamp
// Elapsed miliseconds since start of scrolling.
var time = timestamp - start
// Get percent of completion in range [0, 1].
var percent = Math.min(time / duration, 1)
// Apply the easing.
// It can cause bad-looking slow frames in browser performance tool, so be careful.
percent = easing(percent)
window.scrollTo(0, startingY + diff * percent)
// Proceed with animation as long as we wanted it to.
if (time < duration) {
window.requestAnimationFrame(step)
}
})
}
//Apply event handlers. Example of firing the scrolling mechanism.
doScrolling('#section1', 1000)
Instead of the normal mouse scroll speed, I'd like to make it slower and smoother and as consistent as it can in modern browsers.
I have already tried using a few plugins like NiceScroll (https://nicescroll.areaaperta.com/).
But after installing it, for some reason it puts an overflow: hidden; on my content and can't scroll anywhere after. I don't need any custom-designed scrollbars. I just need the scrolling to be smoother when using the mouse scroll or vertical scroll bar like this:
http://pervolo.com/
Would anyone please enlighten me about this? I plan to use the skrollr plugin (https://github.com/Prinzhorn/skrollr) along with the smooth scrolling.
This may get you going:
$(window).on('mousewheel DOMMouseScroll', function(e) {
var dir,
amt = 100;
e.preventDefault();
if(e.type === 'mousewheel') {
dir = e.originalEvent.wheelDelta > 0 ? '-=' : '+=';
}
else {
dir = e.originalEvent.detail < 0 ? '-=' : '+=';
}
$('html, body').stop().animate({
scrollTop: dir + amt
},500, 'linear');
})
DOMMouseScroll and e.originalEvent.detail are required for Firefox. Change amt to be your desired scroll distance, and change 500 to be your desired scroll speed.
Fiddle: http://jsfiddle.net/utcsdyp1/1
I succeeded in making the scroll by mouse wheel look smooth as butter! Copy-paste the following snippet and try yourself.
let easeInOutQuint = t => t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t; // Easing function found at https://gist.github.com/gre/1650294
// With this attempt I tried to make the scroll by mouse wheel look smooth
let delay = ms => new Promise(res => setTimeout(res, ms));
let dy = 0;
window.addEventListener("wheel", async e => {
// Prevent the default way to scroll the page
e.preventDefault();
dy += e.deltaY;
let _dy = dy; // Store the value of "dy"
await delay(150); // Wait for .15s
// If the value hasn't changed during the delay, then scroll to "start + dy"
if (_dy === dy) {
let start = window.scrollY || window.pageYOffset;
customScrollTo(start + dy, 600, easeInOutQuint);
dy = 0;
}
}, { passive: false });
function customScrollTo(to, duration, easingFunction) {
let start = window.scrollY || window.pageYOffset;
let time = Date.now();
let timeElapsed = 0;
let speed = (to - start) / duration;
(function move() {
if (timeElapsed > duration) {
return;
}
timeElapsed = Date.now() - time;
// Get the displacement of "y"
let dy = speed * timeElapsed;
let y = start + dy;
// Map "y" into a range from 0 to 1
let _y = (y - start) / (to - start);
// Fit "_y" into a curve given by "easingFunction"
_y = easingFunction(_y);
// Expand "_y" into the original range
y = start + (to - start) * _y;
window.scrollTo(0, y);
window.requestAnimationFrame(move);
})();
}
You can catch an action when user scrolls using $(element).on( "scroll", handler ), then, using, for example this code
$('html, body').animate({
scrollTop: $("#target-element").offset().top
}, 1000);
scroll to some element using jQuery
Reading the AngularJS docs I haven't figured out if $anchorScroll can have a duration/easing option to smooth scroll to elements.
It only says:
$location.hash('bottom');
// call $anchorScroll()
$anchorScroll();
I do not use jquery and don't want to; is there still a clever yet simple way to make or extend $anchorScroll in order to make scrolling more smooth?
Unfortunately this is not possible using $anchorScroll. As you discovered $anchorScroll doesn't have any options and doesn't work with $ngAnimate. In order to animate the scroll you would need to use your own service/factory or just straight javascript.
For the sake of self-learning I put together an example with a smooth scrolling service. There are probably better ways to do this so any feedback is encouraged.
To scroll to an element you attach a ng-click="gotoElement(ID)" to any element. I think an even better route would be to make this a directive.
Here's the working example on jsFiddle.
Update
There are now a number of third-party directives for accomplishing this.
https://github.com/oblador/angular-scroll.
https://github.com/d-oliveros/ngSmoothScroll
https://github.com/arnaudbreton/angular-smoothscroll
https://gist.github.com/justinmc/d72f38339e0c654437a2
You can also use the angular-scroll, link "https://github.com/durated/angular-scroll/". It is smooth scrolling also few easing functions to get a professional look.
The answer from Brett worked great for me. I did some small changes on his solution in terms of modularization and testability.
Here's is yet another working example on JsFiddle that includes the other version with testing included.
For testing, I'm using Karma and Jasmine. Signature has been slightly modified as follows:
anchorSmoothScroll.scrollTo(elementId, speed);
Where element is a mandatory attribute to scroll to and speed is optional where the default is 20 (as it was before).
You can also use ngSmoothScroll, link: https://github.com/d-oliveros/ngSmoothScroll.
Just include the smoothScroll module as a dependency and use it like this:
Click me!
None of the solutions here actually does what OP originally asked, that is, make $anchorScroll scrolling smoothly.
Difference between smooth scrolling directives and $anchroScroll is that it uses/modifies $location.hash(), which may be desirable in some cases.
Here is gist for simple module that replaces $anchorScroll scrolling with smooth scrolling. It uses https://github.com/oblador/angular-scroll library for the scrolling itself (replace it with something else if you want, it should be easy).
https://gist.github.com/mdvorak/fc8b531d3e082f3fdaa9
Note: It actually does not get $anchorScroll to scroll smoothly, but it replaces its handler for scrolling.
Enable it simply by referencing mdvorakSmoothScroll module in your application.
Alan, thank you. If anyone interested, I formatted it based on John Pappa standards.
(function() {
'use strict';
var moduleId = 'common';
var serviceId = 'anchorSmoothScroll';
angular
.module(moduleId)
.service(serviceId, anchorSmoothScroll);
anchorSmoothScroll.$inject = ['$document', '$window'];
function anchorSmoothScroll($document, $window) {
var document = $document[0];
var window = $window;
var service = {
scrollDown: scrollDown,
scrollUp: scrollUp,
scrollTo: scrollTo,
scrollToTop: scrollToTop
};
return service;
function getCurrentPagePosition(currentWindow, doc) {
// Firefox, Chrome, Opera, Safari
if (currentWindow.pageYOffset) return currentWindow.pageYOffset;
// Internet Explorer 6 - standards mode
if (doc.documentElement && doc.documentElement.scrollTop)
return doc.documentElement.scrollTop;
// Internet Explorer 6, 7 and 8
if (doc.body.scrollTop) return doc.body.scrollTop;
return 0;
}
function getElementY(doc, element) {
var y = element.offsetTop;
var node = element;
while (node.offsetParent && node.offsetParent !== doc.body) {
node = node.offsetParent;
y += node.offsetTop;
}
return y;
}
function scrollDown(startY, stopY, speed, distance) {
var timer = 0;
var step = Math.round(distance / 25);
var leapY = startY + step;
for (var i = startY; i < stopY; i += step) {
setTimeout('window.scrollTo(0, ' + leapY + ')', timer * speed);
leapY += step;
if (leapY > stopY) leapY = stopY;
timer++;
}
};
function scrollUp(startY, stopY, speed, distance) {
var timer = 0;
var step = Math.round(distance / 25);
var leapY = startY - step;
for (var i = startY; i > stopY; i -= step) {
setTimeout('window.scrollTo(0, ' + leapY + ')', timer * speed);
leapY -= step;
if (leapY < stopY) leapY = stopY;
timer++;
}
};
function scrollToTop(stopY) {
scrollTo(0, stopY);
};
function scrollTo(elementId, speed) {
var element = document.getElementById(elementId);
if (element) {
var startY = getCurrentPagePosition(window, document);
var stopY = getElementY(document, element);
var distance = stopY > startY ? stopY - startY : startY - stopY;
if (distance < 100) {
this.scrollToTop(stopY);
} else {
var defaultSpeed = Math.round(distance / 100);
speed = speed || (defaultSpeed > 20 ? 20 : defaultSpeed);
if (stopY > startY) {
this.scrollDown(startY, stopY, speed, distance);
} else {
this.scrollUp(startY, stopY, speed, distance);
}
}
}
};
};
})();
I am not aware of how to animate $anchorScroll . Here's how I do it in my projects:
/* Scroll to top on each ui-router state change */
$rootScope.$on('$stateChangeStart', function() {
scrollToTop();
});
And the JS function:
function scrollToTop() {
if (typeof jQuery == 'undefined') {
return window.scrollTo(0,0);
} else {
var body = $('html, body');
body.animate({scrollTop:0}, '600', 'swing');
}
log("scrollToTop");
return true;
}
We can use JQuery and Javascript with Directive to perform the scrolling to a specific div on anchor tag click.
Please check the working example on the below link -
https://stackoverflow.com/a/67513880/6656918
I used this jquery to move a div by every click.
$(document).ready(function(){
$('#hero').click(function(){
$(this).animate({
left: '+=50px'
},300);
})
})
I'd like to avoid jquery whenever it is possible to get deeper into pure JS.
Is there anyway to achieve the same effect without using jquery?
I know that this will be more complex, but just trying to learn.
you can use the same technique in javascript:
// get the object refrence
var hero_obj = document.getElementById('hero');
// attach the onclick event
hero_obj.onclick = function(){
this.style.left = ( parseInt(this.style.left, 10) + 50 ) + 'px'
};
However, the effect won't be as smooth as jquery
I've stumbled upon this gem on vanilla-js.com a few weeks ago:
var s = document.getElementById('thing').style;
s.opacity = 1;
(function(){(s.opacity-=.1)<0?s.display="none":setTimeout(arguments.callee,40)})();
I really like the simplicity and the size of the code. Elegant and efficient!
I've created a function that affects the left property of an element of your choice based on the code above:
/* element: DOM element such as document.getElementById('hero')
distance: distance in pixels to move to the left such as 50 or 100 */
function moveBy(element, distance){
var target = isNaN(parseInt(s.left)) ? distance : parseInt(s.left) + distance;
(function(){
s.left = isNaN(parseInt(s.left)) ? '1px' : (parseInt(s.left) + 1).toString() + 'px';
if(parseInt(s.left) <= distance) setTimeout(arguments.callee, 40);
})();
}
You can play around and see what fits to your liking in terms of speed and smoothness. Try it here on a jsfiddle.
/* So you go: */
moveBy(document.getElementById('hero'), 50);
/* Or you can bind it to an event */
document.getElementById('hero').addEventListener('click', function(event){
moveBy(this, 50);
});
What a solution like this would need if you're willing to make it better is to replace the left property by translate. As Paul Irish states on his blog, translate provides way better performance than moving elements around with TRBL (top-left-bottom-right). Some sort of easing functions could be added as well to smooth things out.
Here's a code with animation. This snippet is only for modern browsers, but it is easy to modify to work with older browsers (IEs) too. (Actually only attachment of the event needs to be fixed.)
window.onload = function () {
var timer, k, intervals, kX, kY,
counter = 0,
hero = document.getElementById('hero'),
posX = hero.offsetLeft,
posY = hero.offsetTop,
anim = function (elem, params) {
posX += kX;
posY += kY;
elem.style.left = posX + 'px';
elem.style.top = posY + 'px';
if (counter > intervals) {
clearInterval(timer);
counter = 0;
} else {
counter++;
}
return;
},
move = function (elem, params) {
if (timer) {
clearInterval(timer);
counter = 0;
}
k = Math.atan2(params.left, params.top);
kX = Math.sin(k);
kY = Math.cos(k);
intervals = Math.floor(Math.sqrt(Math.pow(params.left, 2) + Math.pow(params.top, 2)));
timer = setInterval(function () {
anim(elem, params);
return;
}, params.speed);
return;
};
document.getElementById('hero').addEventListener('click', function (e) {
move(e.currentTarget, {left: 50, top: 0, speed: 0});
return;
}, false);
return;
}
As you can see, with this code you can also move elements vertical and adjust speed. To switch direction, just add - to corresponding property. The code is using pixels only as units, but that's easy to modify if needed.
It's also easy to convert this functional code to an object. Also jQuery-like duration can be added by passing property params.duration instead of params.speed and doing some advanced calculations with that and kX, kY.
Working demo at jsFiddle
I needed to create an animation solution with easing a while back without using a framework.
The tricky part for me was coping with interrupting/restarting animations part way through when they are tied to user interactions. I found that you can run into trouble pretty quickly if your animations double-fire.
Here is on github: https://github.com/robCrawford/js-anim
There are a few supporting functions but here's the main animation:
function animate(el, prop, to, pxPerSecond, easing, callback){
/**
* Animate style property
* i.e. animate(div1, "width", 1100, 1000, "out", function(){console.log('div1 anim end')});
*
* #param el DOM element
* #param prop Property to animate
* #param to Destination property value
* #param pxPerSecond Speed of animation in pixels per second
* #param easing (optional) Easing type: "in" or "out"
* #param callback (optional) Function to call when animation is complete
*/
var frameDur = 10,
initPropVal = parseInt(getCurrCss(el, prop)),
distance = Math.abs(to-initPropVal),
easeVal = (easing==="in")?1.5:(easing==="out")?0.5:1, // >1 ease-in, <1 ease-out
elAnimData = getData(el, 'animData');
//Quit if already at 'to' val (still fire callback)
if(initPropVal===to){
if(callback)callback.call();
return;
}
//Init animData for el if first anim
if(!elAnimData){
elAnimData = {};
setData(el, {'animData':elAnimData});
}
//Get data for prop being animated or create entry
var animDataOb = elAnimData[prop];
if(!animDataOb)animDataOb = elAnimData[prop] = {};
//Don't re-initialise an existing animation i.e. same prop/to
if(animDataOb.to === to)return;
animDataOb.to = to; //Store 'to' val
//Clear any exisiting interval
if(animDataOb.intId){
clearInterval(animDataOb.intId);
animDataOb.intId = null;
}
//Create new anim
animDataOb.intId = (function(animDataOb){
var totalSteps = Math.round((distance/pxPerSecond)/(frameDur*.001)),
thisStep = 0;
return setInterval(function(){
var newVal = easeInOut(initPropVal, to, totalSteps, thisStep++, easeVal);
if(!isNaN(newVal))el.style[prop] = newVal + "px"; //Allow 0
if(thisStep > totalSteps)endAnim(animDataOb, callback);
}, frameDur);
})(animDataOb);
}
function endAnim(animDataOb, callback){
//End anim
clearInterval(animDataOb.intId);
animDataOb.intId = animDataOb.to = null;
if(callback)callback.call();
}
I'm coding up a page where I only want to use raw JavaScript code for UI without any interference of plugins or frameworks.
And now I'm struggling with finding a way to scroll over the page smoothly without jQuery.
Native browser smooth scrolling in JavaScript is 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'
});
Try this smooth scrolling demo, or an algorithm like:
Get the current top location using self.pageYOffset
Get the position of element till where you want to scroll to: element.offsetTop
Do a for loop to reach there, which will be quite fast or use a timer to do smooth scroll till that position using window.scrollTo
See also the other popular answer to this question.
Andrew Johnson's 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:
https://www.sitepoint.com/smooth-scrolling-vanilla-javascript/
https://github.com/zengabor/zenscroll/blob/dist/zenscroll.js
https://github.com/cferdinandi/smooth-scroll/blob/master/src/js/smooth-scroll.js
https://github.com/alicelieutier/smoothScroll/blob/master/smoothscroll.js
Algorithm
Scrolling an element requires changing its scrollTop value over time. For a given point in time, calculate a new scrollTop value. To animate smoothly, 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 is the time the animation started;
end_time is when the animation will end (start_time + duration);
start_top is the scrollTop value at the beginning; and
distance is the difference between the desired end value and the start value (target - start_top).
A robust solution should detect when animating is interrupted, and more. Read my post about Smooth Scrolling without jQuery for details.
Demo
See the JSFiddle.
Implementation
The code:
/**
Smoothly scroll element to the given target (element.scrollTop)
for the given duration
Returns a promise that's 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's 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);
});
}
You can use the new Scroll Behaviour CSS Property.
for example, add the below line to your CSS.
html{
scroll-behavior:smooth;
}
and this will result in a native smooth scrolling feature.
see demo here
All modern browsers support the scroll-behavior property.
Read More about Scroll behavior
I've made 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;
}
}
}
Modern browsers has support for CSS "scroll-behavior: smooth" property. So, we even don't need any Javascript at all for this. Just add this for the "html" element, and use usual anchors and links.
scroll-behavior MDN docs
I recently set out to solve this problem in a situation where jQuery wasn't an option, so I'm logging my solution here just 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 Revealing Module Pattern to pass the target element's id to its scrolling() function, via scroll.To('id'), which sets the values used by the scroller() function.
Breakdown
In scrolling():
el : the target DOM object
elPos : returns a function via elememtPosition() which gives the position of the target element relative to the top of the page each time it's called.
duration : transition time in milliseconds.
increment : divides the starting position of the target element into 40 steps.
time : sets the timing of each step.
prev : the target element's previous position in scroller().
E : holds the target element's position in scroller().
The actual work is done by 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 no more.
Each time scroller() is called it checks the current position of the target element (held in variable E) and if that is > 1 OR < -1 and if the page is still scrollable shifts the window by increment pixels - up or down depending if E is a positive or negative value. When E is neither > 1 OR < -1, or E === prev the function stops. I added the DOMElement.scrollTo() method on completion just to make sure the target element was bang on the top of the window (not that you'd notice it being out by a fraction of a pixel!).
The if statement on line 2 of scroller() checks to see if the page is scrolling (in cases where the target might be towards the bottom of the page and the page can scroll no further) by checking E against its previous position (prev).
The ternary condition below it reduce the increment value as E approaches zero. This stops the page overshooting one way and then bouncing back to overshoot the other, and then bouncing back to overshoot the other again, ping-pong style, to infinity and beyond.
If your page is more that c.4000px high you might want to increase the values in the ternary expression's first condition (here at +/-20) and/or the divisor which sets the increment value (here at 40).
Playing about with duration, the divisor which sets increment, and the values in the ternary condition of scroller() should allow you to tailor the function to suit your page.
JSFiddle
N.B.Tested in up-to-date versions of Firefox and Chrome on Lubuntu, and Firefox, Chrome and IE on Windows8.
I've made something like this.
I have no idea if its working 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 attribute "href" if yes,
get rid of "#"
elem—pointer variable without "#"
elemOffset—offset of "scroll to" element from the top of the page
You can use
document.querySelector('your-element').scrollIntoView({behavior: 'smooth'});
If you want to scroll top the top of the page, you can just place an empty element in the top, and smooth scroll to that one.
With using the following smooth scrolling is working fine:
html {
scroll-behavior: smooth;
}
<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 of the div that is used to scroll
Note:
For making the body to scroll give the body an ID.
Here is my solution. Works in most browsers
document.getElementById("scrollHere").scrollIntoView({behavior: "smooth"});
Docs
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>
There are many different methods for smooth scrolling in JavaScript. The most common ones are listed below.
To scroll to a certain position in an exact amount of time, window.requestAnimationFrame can be put to use, calculating the appropriate current position each time. setTimeout can be used to a similar effect when requestAnimationFrame is not supported. (To scroll to a specific element with the function below, just set the position to element.offsetTop.)
/*
#param pos: the y-position to scroll to (in pixels)
#param time: the exact amount of time the scrolling will take (in milliseconds)
*/
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
Demo:
/*
#param time: the exact amount of time the scrolling will take (in milliseconds)
#param pos: the y-position to scroll to (in pixels)
*/
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 300)">
Scroll To Div (300ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 200)">
Scroll To Div (200ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 100)">
Scroll To Div (100ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 50)">
Scroll To Div (50ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 1000)">
Scroll To Div (1000ms)
</button>
<div style="margin: 500px 0px;">
DIV<p/>
<button onClick="scrollToSmoothly(0, 500)">
Back To Top
</button>
<button onClick="scrollToSmoothly(document.body.scrollHeight)">
Scroll To Bottom
</button>
</div>
<div style="margin: 500px 0px;">
</div>
<button style="margin-top: 100px;" onClick="scrollToSmoothly(500, 3000)">
Scroll To y-position 500px (3000ms)
</button>
For more complex cases, the SmoothScroll.js library can be used, which handles smooth scrolling both vertically and horizontally, scrolling inside other container elements, different easing behaviors, scrolling relatively from the current position, and more.
var easings = document.getElementById("easings");
for(var key in smoothScroll.easing){
if(smoothScroll.easing.hasOwnProperty(key)){
var option = document.createElement('option');
option.text = option.value = key;
easings.add(option);
}
}
document.getElementById('to-bottom').addEventListener('click', function(e){
smoothScroll({yPos: 'end', easing: easings.value, duration: 2000});
});
document.getElementById('to-top').addEventListener('click', function(e){
smoothScroll({yPos: 'start', easing: easings.value, duration: 2000});
});
<script src="https://cdn.jsdelivr.net/gh/LieutenantPeacock/SmoothScroll#1.2.0/src/smoothscroll.min.js" integrity="sha384-UdJHYJK9eDBy7vML0TvJGlCpvrJhCuOPGTc7tHbA+jHEgCgjWpPbmMvmd/2bzdXU" crossorigin="anonymous"></script>
<!-- Taken from one of the library examples -->
Easing: <select id="easings"></select>
<button id="to-bottom">Scroll To Bottom</button>
<br>
<button id="to-top" style="margin-top: 5000px;">Scroll To Top</button>
Alternatively, you can pass an options object to window.scroll which scrolls to a specific x and y position and window.scrollBy which scrolls a certain amount from the current position:
// Scroll to specific values
// scrollTo is the same
window.scroll({
top: 2500,
left: 0,
behavior: 'smooth'
});
// Scroll certain amounts from current position
window.scrollBy({
top: 100, // could be negative value
left: 0,
behavior: 'smooth'
});
Demo:
<button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
var elem = document.querySelector("div");
window.scroll({
top: elem.offsetTop,
left: 0,
behavior: 'smooth'
});
}
</script>
If you only need to scroll to an element, not a specific position in the document, you can use Element.scrollIntoView with behavior set to smooth.
document.getElementById("elemID").scrollIntoView({
behavior: 'smooth'
});
Demo:
<button onClick="scrollToDiv()">Scroll To Element</button>
<div id="myDiv" style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
document.getElementById("myDiv").scrollIntoView({
behavior: 'smooth'
});
}
</script>
Modern browsers support the scroll-behavior CSS property, which can be used to make scrolling in the document smooth (without the need for JavaScript). Anchor tags can be used for this by giving the anchor tag a href of # plus the id of the element to scroll to). You can also set the scroll-behavior property for a specific container like a div to make its contents scroll smoothly.
Demo:
html, body{
scroll-behavior: smooth;
}
Scroll To Element
<div id="elem" style="margin: 500px 0px;">Div</div>
Here's my variation:
let MenuItem = function ( _menuItem ) {
// I had a sticky header, so its height had to be taken into account when scrolling
let _header = document.querySelector('.site-header');
let _scrollToBlock = function( e, menuItem ) {
let id = menuItem.getAttribute('href'), // the href attribute stores the id of the block to which the scroll will be
headerHeight = _header.offsetHeight; // determine the height of the header
id = id.replace(/#/, ''); // remove the # sign from the id block
let elem = document.getElementById( id ), // define the element to which we will scroll
top = elem.getBoundingClientRect().top + window.scrollY - headerHeight; // determine the height of the scroll
window.scroll({
top: top,
left: 0,
behavior: 'smooth'
});
},
_addEvents = function() {
_menuItem.addEventListener('click', function (e){
e.preventDefault(); // Disable redirect on click
_scrollToBlock(e, _menuItem);
});
},
_init = function() {
_addEvents();
};
_init();
};
// Initialize the class MenuItem to all links with class .menu__item
document.querySelectorAll('.menu__item').forEach( function(item) {
new MenuItem(item);
} );
Here's the code that worked for me.
`$('a[href*="#"]')
.not('[href="#"]')
.not('[href="#0"]')
.click(function(event) {
if (
location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
&&
location.hostname == this.hostname
) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
event.preventDefault();
$('html, body').animate({
scrollTop: target.offset().top
}, 1000, function() {
var $target = $(target);
$target.focus();
if ($target.is(":focus")) {
return false;
} else {
$target.attr('tabindex','-1');
$target.focus();
};
});
}
}
});
`