How do I know when I've stopped scrolling?

JavascriptScrollDom Events

Javascript Problem Overview


How do I know when I've stopped scrolling using Javascript?

Javascript Solutions


Solution 1 - Javascript

You can add an event handler for the scroll event and start a timeout. Something like:

var timer = null;
window.addEventListener('scroll', function() {
    if(timer !== null) {
        clearTimeout(timer);        
    }
    timer = setTimeout(function() {
          // do something
    }, 150);
}, false);

This will start a timeout and wait 150ms. If a new scroll event occurred in the meantime, the timer is aborted and a new one is created. If not, the function will be executed. You probably have to adjust the timing.

Also note that IE uses a different way to attach event listeners, this should give a good introduction: quirksmode - Advanced event registration models

Solution 2 - Javascript

There isn't a "Stopped Scrolling" event. If you want to do something after the user has finished scrolling, you can set a timer in the "OnScroll" event. If you get another "OnScroll" event fired then reset the timer. When the timer finally does fire, then you can assume the scrolling has stopped. I would think 500 milliseconds would be a good duration to start with.

Here's some sample code that works in IE and Chrome:

<html>

<body onscroll="bodyScroll();">

  <script language="javascript">
    var scrollTimer = -1;

    function bodyScroll() {
      document.body.style.backgroundColor = "white";

      if (scrollTimer != -1)
        clearTimeout(scrollTimer);

      scrollTimer = window.setTimeout("scrollFinished()", 500);
    }

    function scrollFinished() {
      document.body.style.backgroundColor = "red";
    }
  </script>

  <div style="height:2000px;">
    Scroll the page down. The page will turn red when the scrolling has finished.
  </div>

</body>

</html>

Solution 3 - Javascript

Here's a more modern, Promise-based solution I found on a repo called scroll-into-view-if-needed

Instead of using addEventListener on the scroll event it uses requestAnimationFrame to watch for frames with no movement and resolves when there have been 20 frames without movement.

function waitForScrollEnd () {
    let last_changed_frame = 0
    let last_x = window.scrollX
    let last_y = window.scrollY

    return new Promise( resolve => {
        function tick(frames) {
            // We requestAnimationFrame either for 500 frames or until 20 frames with
            // no change have been observed.
            if (frames >= 500 || frames - last_changed_frame > 20) {
                resolve()
            } else {
                if (window.scrollX != last_x || window.scrollY != last_y) {
                    last_changed_frame = frames
                    last_x = window.scrollX
                    last_y = window.scrollY
                }
                requestAnimationFrame(tick.bind(null, frames + 1))
            }
        }
        tick(0)
    })
}

With async/await and then

await waitForScrollEnd()

waitForScrollEnd().then(() => { /* Do things */ })

Solution 4 - Javascript

(function( $ ) {
        $(function() {
            var $output = $( "#output" ),
                scrolling = "<span id='scrolling'>Scrolling</span>",
                stopped = "<span id='stopped'>Stopped</span>";
                $( window ).scroll(function() {
                    $output.html( scrolling );
                    clearTimeout( $.data( this, "scrollCheck" ) );
                    $.data( this, "scrollCheck", setTimeout(function() {
                        $output.html( stopped );
                    }, 250) );
    
                });
        });
    })( jQuery );

=======>>>> Working Example here

Solution 5 - Javascript

I did something like this:

var scrollEvents = (function(document, $){

    var d = {
        scrolling: false,
        scrollDirection : 'none',
        scrollTop: 0,
        eventRegister: {
            scroll: [],
            scrollToTop: [],
            scrollToBottom: [],
            scrollStarted: [],
            scrollStopped: [],
            scrollToTopStarted: [],
            scrollToBottomStarted: []
        },
        getScrollTop: function(){ 
            return d.scrollTop;
        },
        setScrollTop: function(y){
            d.scrollTop = y;
        },
        isScrolling: function(){
            return d.scrolling;
        },
        setScrolling: function(bool){
            var oldVal = d.isScrolling();
            d.scrolling = bool;
            if(bool){
                d.executeCallbacks('scroll');
                if(oldVal !== bool){
                    d.executeCallbacks('scrollStarted');
                }
            }else{
                d.executeCallbacks('scrollStopped');
            }
        },
        getScrollDirection : function(){
            return d.scrollDirection;
        },
        setScrollDirection : function(direction){
            var oldDirection = d.getScrollDirection();
            d.scrollDirection = direction;
            if(direction === 'UP'){
                d.executeCallbacks('scrollToTop');
                if(direction !== oldDirection){
                    d.executeCallbacks('scrollToTopStarted');
                }
            }else if(direction === 'DOWN'){
                d.executeCallbacks('scrollToBottom');
                if(direction !== oldDirection){
                    d.executeCallbacks('scrollToBottomStarted');
                }
            }
        },
        init : function(){
            d.setScrollTop($(document).scrollTop());
            var timer = null;
            $(window).scroll(function(){
                d.setScrolling(true);
                var x = d.getScrollTop();
                setTimeout(function(){
                    var y = $(document).scrollTop();
                    d.setScrollTop(y);
                    if(x > y){
                        d.setScrollDirection('UP');
                    }else{
                        d.setScrollDirection('DOWN');
                    }
                }, 100);
                if(timer !== 'undefined' && timer !== null){
                    clearTimeout(timer);
                }
                timer = setTimeout(function(){
                    d.setScrolling(false);
                    d.setScrollDirection('NONE');
                }, 200);
            });
        },
        registerEvents : function(eventName, callback){
            if(typeof eventName !== 'undefined' && typeof callback === 'function' && typeof d.eventRegister[eventName] !== 'undefined'){
                d.eventRegister[eventName].push(callback);
            }
        },
        executeCallbacks: function(eventName){
            var callabacks = d.eventRegister[eventName];
            for(var k in callabacks){
                if(callabacks.hasOwnProperty(k)){
                    callabacks[k](d.getScrollTop());
                }
            }
        }
    };
    return d;

})(document, $);

the code is available here: documentScrollEvents

Solution 6 - Javascript

Minor update in your answer. Use mouseover and out function.

 $(document).ready(function() {
           function ticker() {
    $('#ticker li:first').slideUp(function() {
        $(this).appendTo($('#ticker')).slideDown();
    });
}

var ticke= setInterval(function(){ 
    						ticker(); 
			}, 3000);
       $('#ticker li').mouseover(function() { 
          clearInterval(ticke);
      }).mouseout(function() { 
          ticke= setInterval(function(){ ticker(); }, 3000);
        });
      
        });

DEMO

Solution 7 - Javascript

I was trying too add a display:block property for social icons that was previously hidden on scroll event and then again hide after 2seconds. But

I too had a same problem as my code for timeout after first scroll would start automatically and did not had reset timeout idea. As it didn't had proper reset function.But after I saw David's idea on this question I was able to reset timeout even if someone again scrolled before actually completing previous timeout.

  1. problem code shown below before solving

    $(window).scroll(function(){
    setTimeout(function(){
    $('.fixed-class').slideUp('slow');
    },2000);
    });
    


  1. edited and working code with reset timer if next scroll occurs before 2s

    var timer=null;
    $(window).scroll(function(){
    $('.fixed-class').css("display", "block");
    if(timer !== null) {
    clearTimeout(timer);
    } timer=setTimeout(function(){ $('.fixed-class').slideUp('slow'); },2000);

});

My working code will trigger a hidden division of class named 'fixed-class' to show in block on every scroll. From start of latest scroll the timer will count 2 sec and then again change the display from block to hidden.

Solution 8 - Javascript

For more precision you can also check the scroll position:

function onScrollEndOnce(callback, target = null) {
	let timeout
	let targetTop
	const startPosition = Math.ceil(document.documentElement.scrollTop)

	if (target) {
		targetTop = Math.ceil(target.getBoundingClientRect().top + document.documentElement.scrollTop)
	}

	function finish(removeEventListener = true) {
		if (removeEventListener) {
			window.removeEventListener('scroll', onScroll)
		}

		callback()
	}

	function isScrollReached() {
		const currentPosition = Math.ceil(document.documentElement.scrollTop)

		if (targetTop == null) {
			return false
		} else if (targetTop >= startPosition) {
			return currentPosition >= targetTop
		} else {
			return currentPosition <= targetTop
		}
	}

	function onScroll() {
		if (timeout) {
			clearTimeout(timeout)
		}

		if (isScrollReached()) {
			finish()
		} else {
			timeout = setTimeout(finish, 500)
		}
	}

	if (isScrollReached()) {
		finish(false)
	} else {
		window.addEventListener('scroll', onScroll)
	}
}

Usage example:

const target = document.querySelector('#some-element')

onScrollEndOnce(() => console.log('scroll end'), target)

window.scrollTo({
	top: Math.ceil(target.getBoundingClientRect().top + document.documentElement.scrollTop),
	behavior: 'smooth',
})

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkrishnaView Question on Stackoverflow
Solution 1 - JavascriptFelix KlingView Answer on Stackoverflow
Solution 2 - JavascriptDavidView Answer on Stackoverflow
Solution 3 - JavascriptSam CarltonView Answer on Stackoverflow
Solution 4 - JavascriptMuhammad TahirView Answer on Stackoverflow
Solution 5 - Javascriptsri_wbView Answer on Stackoverflow
Solution 6 - JavascriptIvin RajView Answer on Stackoverflow
Solution 7 - JavascriptAhsit TamangView Answer on Stackoverflow
Solution 8 - JavascriptMihail H.View Answer on Stackoverflow