Add and Remove class on window scroll [duplicate] - javascript

So basically I'd like to remove the class from 'header' after the user scrolls down a little and add another class to change it's look.
Trying to figure out the simplest way of doing this but I can't make it work.
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll <= 500) {
$(".clearheader").removeClass("clearHeader").addClass("darkHeader");
}
}
CSS
.clearHeader{
height: 200px;
background-color: rgba(107,107,107,0.66);
position: fixed;
top:200;
width: 100%;
}
.darkHeader { height: 100px; }
.wrapper {
height:2000px;
}
HTML
<header class="clearHeader"> </header>
<div class="wrapper"> </div>
I'm sure I'm doing something very elementary wrong.

$(window).scroll(function() {
var scroll = $(window).scrollTop();
//>=, not <=
if (scroll >= 500) {
//clearHeader, not clearheader - caps H
$(".clearHeader").addClass("darkHeader");
}
}); //missing );
Fiddle
Also, by removing the clearHeader class, you're removing the position:fixed; from the element as well as the ability of re-selecting it through the $(".clearHeader") selector. I'd suggest not removing that class and adding a new CSS class on top of it for styling purposes.
And if you want to "reset" the class addition when the users scrolls back up:
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 500) {
$(".clearHeader").addClass("darkHeader");
} else {
$(".clearHeader").removeClass("darkHeader");
}
});
Fiddle
edit: Here's version caching the header selector - better performance as it won't query the DOM every time you scroll and you can safely remove/add any class to the header element without losing the reference:
$(function() {
//caches a jQuery object containing the header element
var header = $(".clearHeader");
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 500) {
header.removeClass('clearHeader').addClass("darkHeader");
} else {
header.removeClass("darkHeader").addClass('clearHeader');
}
});
});
Fiddle

Pure javascript
Here's javascript-only example of handling classes during scrolling.
const navbar = document.getElementById('navbar')
// OnScroll event handler
const onScroll = () => {
// Get scroll value
const scroll = document.documentElement.scrollTop
// If scroll value is more than 0 - add class
if (scroll > 0) {
navbar.classList.add("scrolled");
} else {
navbar.classList.remove("scrolled")
}
}
// Use the function
window.addEventListener('scroll', onScroll)
#navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 60px;
background-color: #89d0f7;
box-shadow: 0px 5px 0px rgba(0, 0, 0, 0);
transition: box-shadow 500ms;
}
#navbar.scrolled {
box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.25);
}
#content {
height: 3000px;
margin-top: 60px;
}
<!-- Optional - lodash library, used for throttlin onScroll handler-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
<header id="navbar"></header>
<div id="content"></div>
Some improvements
You'd probably want to throttle handling scroll events, more so as handler logic gets more complex, in that case throttle from lodash lib comes in handy.
And if you're doing spa, keep in mind that you need to clear event listeners with removeEventListener once they're not needed (eg during onDestroy lifecycle hook of your component, like destroyed() for Vue, or maybe return function of useEffect hook for React).
Example throttling with lodash:
// Throttling onScroll handler at 100ms with lodash
const throttledOnScroll = _.throttle(onScroll, 100, {})
// Use
window.addEventListener('scroll', throttledOnScroll)

Add some transition effect to it if you like:
http://jsbin.com/boreme/17/edit?html,css,js
.clearHeader {
height:50px;
background:lightblue;
position:fixed;
top:0;
left:0;
width:100%;
-webkit-transition: background 2s; /* For Safari 3.1 to 6.0 */
transition: background 2s;
}
.clearHeader.darkHeader {
background:#000;
}

Its my code
jQuery(document).ready(function(e) {
var WindowHeight = jQuery(window).height();
var load_element = 0;
//position of element
var scroll_position = jQuery('.product-bottom').offset().top;
var screen_height = jQuery(window).height();
var activation_offset = 0;
var max_scroll_height = jQuery('body').height() + screen_height;
var scroll_activation_point = scroll_position - (screen_height * activation_offset);
jQuery(window).on('scroll', function(e) {
var y_scroll_pos = window.pageYOffset;
var element_in_view = y_scroll_pos > scroll_activation_point;
var has_reached_bottom_of_page = max_scroll_height <= y_scroll_pos && !element_in_view;
if (element_in_view || has_reached_bottom_of_page) {
jQuery('.product-bottom').addClass("change");
} else {
jQuery('.product-bottom').removeClass("change");
}
});
});
Its working Fine

Is this value intended? if (scroll <= 500) { ... This means it's happening from 0 to 500, and not 500 and greater. In the original post you said "after the user scrolls down a little"

In a similar case, I wanted to avoid always calling addClass or removeClass due to performance issues. I've split the scroll handler function into two individual functions, used according to the current state. I also added a debounce functionality according to this article: https://developers.google.com/web/fundamentals/performance/rendering/debounce-your-input-handlers
var $header = jQuery( ".clearHeader" );
var appScroll = appScrollForward;
var appScrollPosition = 0;
var scheduledAnimationFrame = false;
function appScrollReverse() {
scheduledAnimationFrame = false;
if ( appScrollPosition > 500 )
return;
$header.removeClass( "darkHeader" );
appScroll = appScrollForward;
}
function appScrollForward() {
scheduledAnimationFrame = false;
if ( appScrollPosition < 500 )
return;
$header.addClass( "darkHeader" );
appScroll = appScrollReverse;
}
function appScrollHandler() {
appScrollPosition = window.pageYOffset;
if ( scheduledAnimationFrame )
return;
scheduledAnimationFrame = true;
requestAnimationFrame( appScroll );
}
jQuery( window ).scroll( appScrollHandler );
Maybe someone finds this helpful.

For Android mobile $(window).scroll(function() and $(document).scroll(function() may or may not work. So instead use the following.
jQuery(document.body).scroll(function() {
var scroll = jQuery(document.body).scrollTop();
if (scroll >= 300) {
//alert();
header.addClass("sticky");
} else {
header.removeClass('sticky');
}
});
This code worked for me. Hope it will help you.

This is based of of #shahzad-yousuf's answer, but I only needed to compress a menu when the user scrolled down. I used the reference point of the top container rolling "off screen" to initiate the "squish"
<script type="text/javascript">
$(document).ready(function (e) {
//position of element
var scroll_position = $('div.mainContainer').offset().top;
var scroll_activation_point = scroll_position;
$(window).on('scroll', function (e) {
var y_scroll_pos = window.pageYOffset;
var element_in_view = scroll_activation_point < y_scroll_pos;
if (element_in_view) {
$('body').addClass("toolbar-compressed ");
$('div.toolbar').addClass("toolbar-compressed ");
} else {
$('body').removeClass("toolbar-compressed ");
$('div.toolbar').removeClass("toolbar-compressed ");
}
});
}); </script>

Related

Navbar hide on scroll with offset

I'm using this code to make my sticky navbar disappear on scroll down and re-appear on scroll up. However this code is pretty precise resulting sometimes in starting one of both animations without actually scrolling.
What I'm trying to achieve is that a user should scroll 20px down before the if statement runs. Same if they would scroll up again...
https://jsfiddle.net/as1tpbjw/2/
const body = document.querySelector("#navbar");;
let lastScroll = 0;
window.addEventListener("scroll", () => {
const currentScroll = window.pageYOffset;
if (currentScroll <= 0) {
body.classList.remove("scroll-up");
return;
}
if (currentScroll > lastScroll && !body.classList.contains("scroll-down")) {
body.classList.remove("scroll-up");
body.classList.add("scroll-down");
} else if (
currentScroll < lastScroll &&
body.classList.contains("scroll-down")
) {
body.classList.remove("scroll-down");
body.classList.add("scroll-up");
}
lastScroll = currentScroll;
});
As far as I can see, in my relatively old version of Firefox, it works well.
I added if (Math.abs(currentScroll - lastScroll) < 20) { return; } and this adds a 20px delay either way.
Also, that scroll-up class doesn't seem to be doing anything in the fiddle.
Update:
If you want an animation, you can replace the CSS for .scroll-down and add a transition to #navbar:
#navbar.scroll-down {
height: 0;
}
#navbar {
/* … */
transition: height .5s;
}
Not only does scroll-up do nothing, but the following code even breaks (doesn't show the navbar) when you scroll to the top:
if (currentScroll <= 0) {
body.classList.remove("scroll-up");
return;
}
You may want to remove it.
const body = document.querySelector("#navbar");
let lastScroll = 0;
window.addEventListener("scroll", () => {
const currentScroll = window.pageYOffset;
if (Math.abs(currentScroll - lastScroll) < 20) {
return;
}
if (currentScroll > lastScroll) {
body.classList.add("scroll-down");
} else {
body.classList.remove("scroll-down");
}
lastScroll = currentScroll;
});
body {
margin: 0;
min-height: 200vh;
}
#navbar.scroll-down {
height: 0;
}
#navbar {
height: 50px;
background: red;
position: sticky;
top: 0;
left: 0;
transition: height .5s;
}
<body>
<div id="navbar">
</div>
</body>

How to fadeOut my element when I am scrolling up

I am trying to make nice parallax website, but this is my first time when I am using jQuery, however I have the following problem:
HTML:
<div class="section4"><h1 class="text-center">Online Marketing</h1></div>
<div class="section4"><p style="text-align:justify">SOME TEXT IS HERE BUT IT DOES NOT IMPORTANT</p></div>
CSS:
.section4 {
margin-bottom: 20px;
position: relative;
opacity: 0;
transform: translateX(-40px);
}
.is-showing{
opacity: 1;
transform: translateX(0px);
transition: all 0.3s ease-in-out;
}
.is-hide {
opacity: 0;
position: relative;
}
JS:
var wScroll = $(this).scrollTop();
if($(window).scrollTop() > 800){
if (wScroll > $('.section4').offset().top - ($(window).height() / 2.2)) {
$('.section4').each(function(i){
setTimeout(function(){
$('.section4').eq(i).addClass('is-showing');
}, 250 * (i+1));
})
}
}
So when I am scrolling down it is work great. It fades in properly, but now I want, when user scroll up, to it disappears. I tried with:
JS :
else {
$('.section4').eq().addClass('is-hide');
})
//$('section4').fadeOut("slow");
}
/*if (wScroll < $('.section4').offset().top + ($(window).height() - 500)) {
$('.section4').addClass('is-hide');
}*/
And lot of simular example, but now I don't have idea how to fix it, so if someone can give me advice or some solution I will appreciate it.
If I wanted to detect scroll direction,, I would do the following:
var lastScrollTop = 0;
var isScrollingDown = false;
$(window).scroll(function (event) {
var currentScrollTop = $(window).scrollTop();
if (lastScrollTop > currentScrollTop ) {
isScrollingDown = false;
} else {
isScrollingDown = true;
}
// Do something
});
You can use this logic within your code to do as required.
In case you are looking third party solution you can check out Wow.js
It is great lightweight library that uses animate.css effects on scrolling.

codepen: jQuery .css() edit when scrolldown past anchor, vice versa

codepen http://codepen.io/mathiasvanderbrempt/pen/mJjjrx
I need to figure out why this jquery isn't working...
I want to make the .navigationfx drop down (top = 0px) when its scrolltop() reaches the first section (should I add a class or id?). Accordingly, I want to make it move up (top = -30px) when it scrolls back up past it.
$(function(){
//last scroll will always save the current scroll position value
var CurrentScroll = 0;
$(window).scroll(function(event){
var NextScroll = $(this).scrollTop();
if (NextScroll > CurrentScroll){
//codes related to down-ward scrolling here
if ($(window).scrollTop() > $('section').scrollTop())
{
$('navigationfx').css('top', '0px')
}
else {
$('navigationfx').css('top', '-100px')
}
}
else {
//codes related to upward-scrolling here
if ($(window).scrollTop() < $('section').scrollTop())
{
$('navigationfx').css('top', '-100px')
}
else {
$('navigationfx').css('top', '0px')
}
}
//Updates current scroll position
CurrentScroll = NextScroll;
});
});
css
.navigationfx {
visibility: ;
position: fixed;
z-index: 2;
margin: 0 auto;
padding: 5px 0 0 0;
width: 100%;
top: -50px;
left: 0;
background: rgba(250,250,250,0.5);
text-align: center;
color: #333;
border-bottom: 1px solid #dddedf
}
Few things:
You select the first section form the matched set as:
$('section').first();
Since, the section has no scroll, you would have to use the offset top as:
$('section').first().offset().top;
Do not scan the DOM over and over for the same element. Store a reference and use it as you need as:
var $nav = $(".navigationfx");
var $firstSection = $('section').first();
Not sure if this is what you wanted, but, here is your pen updated
P.S. It's okay to use .css() to add/update top for this specific scenario and doesn't have to be add/remove a class.
Because $('section').scrollTop() permanently equal 0.
Change it to $("section").offset().top.
http://codepen.io/anon/pen/jPpeKv
When you log $('section').scrollTop() to the console, you'll see that it always returns zero. Instead, use $('section').offset().top.
You are also missing a . in the navigationfx selector.
$(function(){
//last scroll will always save the current scroll position value
var CurrentScroll = 0;
$(window).scroll(function(event){
var NextScroll = $(this).scrollTop();
if (NextScroll > CurrentScroll){
//codes related to down-ward scrolling here
if($(window).scrollTop() > $('section').offset().top)
{
$('.navigationfx').css('top', '0px')
}
else {
$('.navigationfx').css('top', '-100px')
}
}
else {
//codes related to upward-scrolling here
if($(window).scrollTop() < $('section').offset().top)
{
$('.navigationfx').css('top', '-100px')
}
else {
$('.navigationfx').css('top', '0px')
}
}
//Updates current scroll position
CurrentScroll = NextScroll;
});
});
$('navigationfx') => $('.navigationfx')
and I think all this code can be shorten to that:
$(function(){
$(window).scroll(function(event){
var offset = $(window).scrollTop() > $('section').offset().top?'0px':'-100px';
$('.navigationfx').css('top', offset);
});
$(window).scroll();
});
codepen: http://codepen.io/anon/pen/YXjJLG

jquery: animate down navigation, not sliding back up/working properly

Hey so i was trying to get my navigation to animate down after a certain div has passed but its not working properly. not sure why. it works when sliding down although a bit buggy(sometimes there seems to be a delay before it slides down and other times it slides down properly immediately). It also does not slide up it justs removes it self.
what am i doing wrong?
here is my code:
$(window).scroll(function () {
targetScroll = $('#scroll_verder').position().top,
currentScroll = $('html').scrollTop() || $('body').scrollTop();
if(currentScroll>=targetScroll){
$('.navbar').addClass('show-menu').animate({ top: '0px' });
}
else {
$('.navbar').stop();
$('.navbar').removeClass('show-menu');
$('.navbar').animate({ top: '-50px' });
}
});
Had a look at your code on the link you posted. This should do the trick:
var reachedTarget = false; // Prevent animation collisions with this
var targetScroll = $('#scroll_verder').position().top;
$(window).scroll(function () {
var currentScroll = $('html').scrollTop() || $('body').scrollTop();
if ( currentScroll >= targetScroll ) {
if ( !reachedTarget ) {
$('.navbar').stop();
$('.navbar').addClass('show-menu').animate({ top: '0px' });
}
reachedTarget = true;
} else{
if ( reachedTarget ) {
$('.navbar').stop();
$('.navbar').removeClass('show-menu').animate({ top: '-50px' });
}
reachedTarget = false;
}
});
EDIT: In CSS (to make sure initial position is correct):
.navbar.show-menu {
z-index: 999;
display: block;
top : -50px;
}

Smooth scroll anchor links WITHOUT jQuery

Is it possible to use smooth scroll to anchor links but without jQuery? I am creating a new site and I don't want to use jQuery.
Extending this answer: https://stackoverflow.com/a/8918062/3851798
After defining your function of scrollTo, you can pass the element you want to scrollTo in the function.
function scrollTo(element, to, duration) {
if (duration <= 0) return;
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) return;
scrollTo(element, to, duration - 10);
}, 10);
}
If you have a div with an id="footer"
<div id="footer" class="categories">…</div>
In the script that you run to scroll you can run this,
elmnt = document.getElementById("footer");
scrollTo(document.body, elmnt.offsetTop, 600);
And there you have it. Smooth scrolling without jQuery. You can actually play around with that code on your browser's console and fine tune it to your liking.
Using the function from here: JavaScript animation and modifying it to modify a property (not only a style's property), you can try something like this:
DEMO: http://jsfiddle.net/7TAa2/1/
Just saying...
function animate(elem, style, unit, from, to, time, prop) {
if (!elem) {
return;
}
var start = new Date().getTime(),
timer = setInterval(function() {
var step = Math.min(1, (new Date().getTime() - start) / time);
if (prop) {
elem[style] = (from + step * (to - from)) + unit;
} else {
elem.style[style] = (from + step * (to - from)) + unit;
}
if (step === 1) {
clearInterval(timer);
}
}, 25);
if (prop) {
elem[style] = from + unit;
} else {
elem.style[style] = from + unit;
}
}
window.onload = function() {
var target = document.getElementById("div5");
animate(document.scrollingElement || document.documentElement, "scrollTop", "", 0, target.offsetTop, 2000, true);
};
div {
height: 50px;
}
<div id="div1">asdf1</div>
<div id="div2">asdf2</div>
<div id="div3">asdf3</div>
<div id="div4">asdf4</div>
<div id="div5">asdf5</div>
<div id="div6">asdf6</div>
<div id="div7">asdf7</div>
<div id="div8">asdf8</div>
<div id="div9">asdf9</div>
<div id="div10">asdf10</div>
<div id="div10">asdf11</div>
<div id="div10">asdf12</div>
<div id="div10">asdf13</div>
<div id="div10">asdf14</div>
<div id="div10">asdf15</div>
<div id="div10">asdf16</div>
<div id="div10">asdf17</div>
<div id="div10">asdf18</div>
<div id="div10">asdf19</div>
<div id="div10">asdf20</div>
Actually, there is more lightweight and simple way to do that:
https://codepen.io/ugg0t/pen/mqBBBY
function scrollTo(element) {
window.scroll({
behavior: 'smooth',
left: 0,
top: element.offsetTop
});
}
document.getElementById("button").addEventListener('click', () => {
scrollTo(document.getElementById("8"));
});
div {
width: 100%;
height: 200px;
background-color: black;
}
div:nth-child(odd) {
background-color: white;
}
button {
position: absolute;
left: 10px;
top: 10px;
}
<div id="1"></div>
<div id="2"></div>
<div id="3"></div>
<div id="4"></div>
<div id="5"></div>
<div id="6"></div>
<div id="7"></div>
<div id="8"></div>
<div id="9"></div>
<div id="10"></div>
<button id="button">Button</button>
This is a pretty old question, but it's important to say that nowadays smooth scrolling is supported in CSS, so there's no need for any scripts:
html {
scroll-behavior: smooth;
}
As noted by #Andiih, as of late 2022 there is full browser support for this.
Use this:
let element = document.getElementById("box");
element.scrollIntoView();
element.scrollIntoView(false);
element.scrollIntoView({block: "end"});
element.scrollIntoView({behavior: "instant", block: "end", inline: "nearest"});
DEMO: https://jsfiddle.net/anderpo/x8ucc5ak/1/
Vanilla js variant using requestAnimationFrame with easings and all browsers supported:
const requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame;
function scrollTo(to) {
const start = window.scrollY || window.pageYOffset
const time = Date.now()
const duration = Math.abs(start - to) / 3;
(function step() {
var dx = Math.min(1, (Date.now() - time) / duration)
var pos = start + (to - start) * easeOutQuart(dx)
window.scrollTo(0, pos)
if (dx < 1) {
requestAnimationFrame(step)
}
})()
}
Any easing supported!
CSS3 transitions with a :target selector can give a nice result without any JS hacking. I was just contemplating whether to imlement this but without Jquery it does get a bit messy. See this question for details.
Try this code here:
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
});
Smooth Scroll behavior with polyfill...
Example:
document.querySelectorAll('a[href^="#"]').addEventListener("click", function(event) {
event.preventDefault();
document.querySelector(this.getAttribute("href")).scrollIntoView({ behavior: "smooth" });
});
Repository: https://github.com/iamdustan/smoothscroll
My favorite scroll-to library currently is Zenscroll because of the wide range of features and small size (currently only 3.17kb).
In the future it may make more sense to use the native scrollIntoView functionality, but since it'd have to be polyfilled in most production sites today due to the lack of IE support, I recommend using Zenscroll instead in all cases.
March 2022
I know this is an old question but wanted to put forward an answer that has simpler ways of doing it in modern days. As of today, almost all the major browsers are compatible with scroll-behavior including Safari with its latest release. Still, you might want to employ fallback methods or just use the javascript approach described in method 2 for compatibility in older browsers.
Method 1: HTML and CSS
You can just do this with
Click
.
.
.
<h2 id="target">Target</h2>
and CSS
html {
scroll-behavior: smooth
}
Method 2: JavaScript
Or if you have a unique case that needs javascript, go on elaborate with this method.
const scrollTrigger = document.getElementById('scroll-trigger');
const target = document.getElementById('target');
scrollTrigger.addEventListener('click', function (e) {
window.scroll({
top: target.offsetTop,
left:0,
behavior: 'smooth' });
}, false)
It's upgraded version from #Ian
// Animated scroll with pure JS
// duration constant in ms
const animationDuration = 600;
// scrollable layout
const layout = document.querySelector('main');
const fps = 12; // in ms per scroll step, less value - smoother animation
function scrollAnimate(elem, style, unit, from, to, time, prop) {
if (!elem) {
return;
}
var start = new Date().getTime(),
timer = setInterval(function () {
var step = Math.min(1, (new Date().getTime() - start) / time);
var value = (from + step * (to - from)) + unit;
if (prop) {
elem[style] = value;
} else {
elem.style[style] = value;
}
if (step === 1) {
clearInterval(timer);
}
}, fps);
if (prop) {
elem[style] = from + unit;
} else {
elem.style[style] = from + unit;
}
}
function scrollTo(hash) {
const target = document.getElementById(hash);
const from = window.location.hash.substring(1) || 'start';
const offsetFrom = document.getElementById(from).offsetTop;
const offsetTo = target.offsetTop;
scrollAnimate(layout,
"scrollTop", "", offsetFrom, offsetTo, animationDuration, true);
setTimeout(function () {
window.location.hash = hash;
}, animationDuration+25)
};
// add scroll when click on menu items
var menu_items = document.querySelectorAll('a.mdl-navigation__link');
menu_items.forEach(function (elem) {
elem.addEventListener("click",
function (e) {
e.preventDefault();
scrollTo(elem.getAttribute('href').substring(1));
});
});
// scroll when open link with anchor
window.onload = function () {
if (window.location.hash) {
var target = document.getElementById(window.location.hash.substring(1));
scrollAnimate(layout, "scrollTop", "", 0, target.offsetTop, animationDuration, true);
}
}
For anyone in 2019,
first, you add an event listener
document.getElementById('id').addEventListener('click', () => scrollTo())
then you target the element and go smoothly to it
function scrollTo() {
let target = document.getElementById('target');
target.scrollIntoView({
behavior: "smooth",
block: "end",
inline: "nearest"
})
}
Based on MDN docs for scroll options we can use the following code:
element.scrollTo({
top: 100,
left: 100,
behavior: 'smooth'
});
In fact, the behavior key can accept smooth and auto variables. first for smooth motion and second for a single jump.
‍‍
Here is a simple solution in pure JavaScript. It takes advantage of CSS property scroll-behavior: smooth
function scroll_to(id) {
document.documentElement.style.scrollBehavior = 'smooth'
element = document.createElement('a');
element.setAttribute('href', id)
element.click();
}
Usage:
Say we have 10 divs:
<div id='df7ds89' class='my_div'>ONE</div>
<div id='sdofo8f' class='my_div'>TWO</div>
<div id='34kj434' class='my_div'>THREE</div>
<div id='gbgfh98' class='my_div'>FOUR</div>
<div id='df89sdd' class='my_div'>FIVE</div>
<div id='34l3j3r' class='my_div'>SIX</div>
<div id='56j5453' class='my_div'>SEVEN</div>
<div id='75j6h4r' class='my_div'>EIGHT</div>
<div id='657kh54' class='my_div'>NINE</div>
<div id='43kjhjh' class='my_div'>TEN</div>
We can scroll to the ID of choice:
scroll_to('#657kh54')
You simply call this function on your click event (e.g. click button then scroll to div #9).
Result:
Of course it looks much smoother in real life.
FIDDLE
Unfortunately, IE and Safari don't support scrollBehavior = 'smooth' as of 2019
MDN Web Docs
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. setTimeout can be used to a similar effect when requestAnimationFrame is not supported.
/*
#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) {
var elem = document.querySelector("div");
scrollToSmoothly(elem.offsetTop);
});
document.getElementById("toTop").addEventListener('click', function(e){
scrollToSmoothly(0, 700);
});
<button id="toElement">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element
<button id="toTop">Scroll back to top</button>
</div>
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.
document.getElementById("toElement").addEventListener('click', function(e) {
smoothScroll({toElement: document.querySelector('div'), duration: 500});
});
document.getElementById("toTop").addEventListener('click', function(e){
smoothScroll({yPos: 0, duration: 700});
});
<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="toTop">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'
});
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>
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.
html, body{
scroll-behavior: smooth;
}
Scroll To Element
<div id="elem" style="margin: 500px 0px;">Div</div>
Without jQuery
const links = document.querySelectorAll('header nav ul a')
for (const link of links) {
link.onclick = function clickHandler(e) {
e.preventDefault()
const href = this.getAttribute('href')
document.querySelector(href).scrollIntoView({ behavior: 'smooth' })
}
}
body {
background-color: black;
height:7000px
}
header {
margin-top: 1.3rem;
margin-bottom: 25rem;
display: flex;
justify-content: center;
align-items: center;
}
nav ul {
display: flex;
}
nav ul li {
all: unset;
margin: 2rem;
cursor: pointer;
}
nav ul li a {
all: unset;
font: bold 1.8rem robto;
color: white;
letter-spacing: 1px;
cursor: pointer;
padding-top: 3rem;
padding-bottom: 2rem;
}
#team,
#contact,
#about {
background-color: #e2df0d;
width: 100%;
height: 35rem;
display: flex;
justify-content: center;
align-items: center;
color: black;
font: bold 4rem roboto;
letter-spacing: 6.2px;
margin-top: 70rem;
}
<header>
<!-- NavBar -->
<nav>
<ul>
<li>Team</li>
<li>Contact</li>
<li>About</li>
</ul>
</nav>
</header>
<!-- ----------- Team ----------------------- -->
<div id="team">
<h2>Team</h2>
</div>
<!-- ----------- Contact ----------------------- -->
<div id="contact">
<h2>Contact</h2>
</div>
<!-- ----------- About ----------------------- -->
<div id="about">
<h2>About</h2>
</div>
Or with just CSS, but it's not supported in all browsers yet
html {scroll-behavior: smooth}
If you want to set all of your deep links # to scroll smoothly you can do this:
const allLinks = document.querySelectorAll('a[href^="#"]')
allLinks.forEach(link => {
const
targetSelector = link.getAttribute('href'),
target = document.querySelector(targetSelector)
if (target) {
link.addEventListener('click', function(e) {
e.preventDefault()
const top = target.offsetTop // consider decreasing your main nav's height from this number
window.scroll({
behavior: 'smooth',
left: 0,
top: top
});
})
}
})
An example code to consider also your main nav's height (this code goes where top const is declared):
const
mainHeader = document.querySelector('header#masthead'), //change to your correct main nav selector
mainHeaderHeight = mainHeader.offsetHeight,
// now calculate top like this:
top = target.offsetTop - mainHeaderHeight
Here is the most elegant and concise solution.
Links:
CSS:
html {
scroll-behavior: smooth;
}
Remember to add a unique id="elementIDtoScrollTo" to each HTML element.

Categories