This question already has answers here: How to detect if browser window is scrolled to bottom? (21 answers) Closed 8 months ago. The community reviewed whether to reopen this question 8 months ago and left it closed: Original close reason(s) were not resolved
I'm making a pagination system (sort of like Facebook) where the content loads when the user scrolls to the bottom. I imagine the best way to do that is to find when the user is at the bottom of the page and run an Ajax query to load more posts.
The only problem is I don't know how to check if the user has scrolled to the bottom of the page. Any ideas?
I'm using jQuery, so feel free to provide answers that use it.
Use the .scroll()
event on window
, like this:
$(window).scroll(function() {
if($(window).scrollTop() + $(window).height() == $(document).height()) {
alert("bottom!");
}
});
You can test it here, this takes the top scroll of the window, so how much it's scrolled down, adds the height of the visible window and checks if that equals the height of the overall content (document
). If you wanted to instead check if the user is near the bottom, it'd look something like this:
$(window).scroll(function() {
if($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
alert("near bottom!");
}
});
You can test that version here, just adjust that 100
to whatever pixel from the bottom you want to trigger on.
I'm not exactly sure why this has not been posted yet, but as per the documentation from MDN, the simplest way is by using native javascript properties:
element.scrollHeight - element.scrollTop === element.clientHeight
Returns true when you're at the bottom of any scrollable element. So simply using javascript:
element.addEventListener('scroll', function(event)
{
var element = event.target;
if (element.scrollHeight - element.scrollTop === element.clientHeight)
{
console.log('scrolled');
}
});
scrollHeight
have wide support in browsers, from ie 8 to be more precise, while clientHeight
and scrollTop
are both supported by everyone. Even ie 6. This should be cross browser safe.
var isAtBottom = ( logField.scrollHeight - logField.scrollTop <= logField.clientHeight + 50 );
. This is useful for auto-scrolling a field only if already at the bottom (so the user can manually examine a specific part of a realtime log without losing their place, etc).
200.181819304947
and 200
). I added a Math.floor()
to help deal with that but I don't know how reliable this will be.
Math.abs(element.scrollHeight - element.scrollTop - element.clientHeight) <= 3.0
(replace 3.0 with whatever pixel tolerance you think is appropriate for your circumstances). This is the way to go because (A) clientHeight
, scrollTop
, and clientHeight
are all rounded which could potentially lead to a 3px error if all align, (B) 3 pixels is hardly visible to the user so the user may think something is wrong with your site when they "think" they are at the bottom of the page when in fact they are not, and (C) certain devices (especially mouseless ones) can have odd behavior when scrolling.
Nick Craver's answer works fine, spare the issue that the value of $(document).height()
varies by browser.
To make it work on all browsers, use this function from James Padolsey:
function getDocHeight() {
var D = document;
return Math.max(
D.body.scrollHeight, D.documentElement.scrollHeight,
D.body.offsetHeight, D.documentElement.offsetHeight,
D.body.clientHeight, D.documentElement.clientHeight
);
}
in place of $(document).height()
, so that the final code is:
$(window).scroll(function() {
if($(window).scrollTop() + $(window).height() == getDocHeight()) {
alert("bottom!");
}
});
min
instead of max
, especially if there's a margin anyways, to avoid accidentally preventing the user from scrolling to what you think is the bottom of the page.
Further to the excellent accepted answer from Nick Craver, you can throttle the scroll event so that it is not fired so frequently thus increasing browser performance:
var _throttleTimer = null;
var _throttleDelay = 100;
var $window = $(window);
var $document = $(document);
$document.ready(function () {
$window
.off('scroll', ScrollHandler)
.on('scroll', ScrollHandler);
});
function ScrollHandler(e) {
//throttle event:
clearTimeout(_throttleTimer);
_throttleTimer = setTimeout(function () {
console.log('scroll');
//do work
if ($window.scrollTop() + $window.height() > $document.height() - 100) {
alert("near bottom!");
}
}, _throttleDelay);
}
Nick Craver's answer needs to be slightly modified to work on iOS 6 Safari Mobile and should be:
$(window).scroll(function() {
if($(window).scrollTop() + window.innerHeight == $(document).height()) {
alert("bottom!");
}
});
Changing $(window).height() to window.innerHeight should be done because when the address bar is hidden an additional 60px are added to the window's height but using $(window).height()
does not reflect this change, while using window.innerHeight
does.
Note: The window.innerHeight
property also includes the horizontal scrollbar's height (if it is rendered), unlike $(window).height()
which will not include the horizontal scrollbar's height. This is not a problem in Mobile Safari, but could cause unexpected behavior in other browsers or future versions of Mobile Safari. Changing ==
to >=
could fix this for most common use cases.
Read more about the window.innerHeight
property here
Here's a fairly simple approach
const didScrollToBottom = elm.scrollTop + elm.clientHeight == elm.scrollHeight
Example
elm.onscroll = function() {
if(elm.scrollTop + elm.clientHeight == elm.scrollHeight) {
// User has scrolled to the bottom of the element
}
}
Where elm
is an element retrieved from i.e document.getElementById
.
value1 : 1200.1111450195312 value2 : 1200
false
even if the user scrolled to the bottom in many cases.
Please check this answer
window.onscroll = function(ev) {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
console.log("bottom");
}
};
You can do footerHeight - document.body.offsetHeight
to see if you are near the footer or reached the footer
Here is a piece of code that will help you debug your code, I tested the above answers and found them to be buggy. I have test the followings on Chrome, IE, Firefox, IPad(Safari). I don't have any others installed to test...
<script type="text/javascript">
$(function() {
$(window).scroll(function () {
var docElement = $(document)[0].documentElement;
var winElement = $(window)[0];
if ((docElement.scrollHeight - winElement.innerHeight) == winElement.pageYOffset) {
alert('bottom');
}
});
});
</script>
There may be a simpler solution, but I stopped at the point at which IT WORKED
If you are still having problems with some rogue browser, here is some code to help you debug:
<script type="text/javascript">
$(function() {
$(window).scroll(function () {
var docElement = $(document)[0].documentElement;
var details = "";
details += '<b>Document</b><br />';
details += 'clientHeight:' + docElement.clientHeight + '<br />';
details += 'clientTop:' + docElement.clientTop + '<br />';
details += 'offsetHeight:' + docElement.offsetHeight + '<br />';
details += 'offsetParent:' + (docElement.offsetParent == null) + '<br />';
details += 'scrollHeight:' + docElement.scrollHeight + '<br />';
details += 'scrollTop:' + docElement.scrollTop + '<br />';
var winElement = $(window)[0];
details += '<b>Window</b><br />';
details += 'innerHeight:' + winElement.innerHeight + '<br />';
details += 'outerHeight:' + winElement.outerHeight + '<br />';
details += 'pageYOffset:' + winElement.pageYOffset + '<br />';
details += 'screenTop:' + winElement.screenTop + '<br />';
details += 'screenY:' + winElement.screenY + '<br />';
details += 'scrollY:' + winElement.scrollY + '<br />';
details += '<b>End of page</b><br />';
details += 'Test:' + (docElement.scrollHeight - winElement.innerHeight) + '=' + winElement.pageYOffset + '<br />';
details += 'End of Page? ';
if ((docElement.scrollHeight - winElement.innerHeight) == winElement.pageYOffset) {
details += 'YES';
} else {
details += 'NO';
}
$('#test').html(details);
});
});
</script>
<div id="test" style="position: fixed; left:0; top: 0; z-index: 9999; background-color: #FFFFFF;">
I hope this will save someone some time.
var docElement = document.documentElement; var winElement = window
winElement.pageYOffset
you can also use winElement.scrollY
.
var elemScrolPosition = elem.scrollHeight - elem.scrollTop - elem.clientHeight;
It calculates distance scroll bar to bottom of element. Equal 0, if scroll bar has reached bottom.
scrollTop
is not rounded (can be fractional) so sometimes the value is not 0 even when scrolled to the bottom.
This is my two cents:
$('#container_element').scroll( function(){
console.log($(this).scrollTop()+' + '+ $(this).height()+' = '+ ($(this).scrollTop() + $(this).height()) +' _ '+ $(this)[0].scrollHeight );
if($(this).scrollTop() + $(this).height() == $(this)[0].scrollHeight){
console.log('bottom found');
}
});
$(this).scrollTop() + $(this).height() == $(this)[0].scrollHeight
=> $(this).scrollTop() + $(this).outerHeight(true) >= $(this)[0].scrollHeight
scrollTop
is not rounded (it can be fractional) while scrollHeight is rounded. This will have false positives.
Here is a vanilla JavaScript solution that uses ES 6 and debounce
:
document.addEventListener('scroll', debounce(() => {
if(document.documentElement.scrollHeight === window.pageYOffset + window.innerHeight) {
// Do something
}
}, 500))
function debounce(e,t=300){let u;return(...i)=>{clearTimeout(u),u=setTimeout(()=>{e.apply(this,i)},t)}}
Demo: https://jsbin.com/jicikaruta/1/edit?js,output
References:
scrollHeight
pageYOffset
innerHeight
Debounce
My solution in plain js:
let el=document.getElementById('el'); el.addEventListener('scroll', function(e) { if (this.scrollHeight - this.scrollTop - this.clientHeight<=0) { alert('Bottom'); } }); #el{ width:400px; height:100px; overflow-y:scroll; }
scrollTop
is not rounded (it can be fractional) while scrollHeight is, so sometimes the calculation will be > 0.
Instead of listening to the scroll event, using Intersection Observer is the inexpensive one for checking if the last element was visible on the viewport (that's mean user was scrolled to the bottom). It also supported for IE7 with the polyfill.
var observer = new IntersectionObserver(function(entries){ if(entries[0].isIntersecting === true) console.log("Scrolled to the bottom"); else console.log("Not on the bottom"); }, { root:document.querySelector('#scrollContainer'), threshold:1 // Trigger only when whole element was visible }); observer.observe(document.querySelector('#scrollContainer').lastElementChild); #scrollContainer{ height: 100px; overflow: hidden scroll; }
.observe(...)
on an event listener like when the user hovering/touching your scroll container so it won't trigger immediately.
In case someone wants a vanilla JavaScript solution and needs to detect when a user has scrolled to the bottom of a <div>
I managed to implement it by using these lines of code
window.addEventListener("scroll", () => {
var offset = element.getBoundingClientRect().top - element.offsetParent.getBoundingClientRect().top;
const top = window.pageYOffset + window.innerHeight - offset;
if (top === element.scrollHeight) {
console.log("bottom");
}
}, { passive: false });
Nick answers its fine but you will have functions which repeats itsself while scrolling or will not work at all if user has the window zoomed. I came up with an easy fix just math.round the first height and it works just as assumed.
if (Math.round($(window).scrollTop()) + $(window).innerHeight() == $(document).height()){
loadPagination();
$(".go-up").css("display","block").show("slow");
}
I Have done this very easy way with pure JS.
function onScroll() {
if (window.pageYOffset + window.innerHeight >= document.documentElement.scrollHeight - 50) {
Console.log('Reached bottom')
}
}
window.addEventListener("scroll", onScroll);
All these solutions doesn't work for me on Firefox and Chrome, so I use custom functions from Miles O'Keefe and meder omuraliev like this:
function getDocHeight()
{
var D = document;
return Math.max(
D.body.scrollHeight, D.documentElement.scrollHeight,
D.body.offsetHeight, D.documentElement.offsetHeight,
D.body.clientHeight, D.documentElement.clientHeight
);
}
function getWindowSize()
{
var myWidth = 0, myHeight = 0;
if( typeof( window.innerWidth ) == 'number' ) {
//Non-IE
myWidth = window.innerWidth;
myHeight = window.innerHeight;
} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
//IE 6+ in 'standards compliant mode'
myWidth = document.documentElement.clientWidth;
myHeight = document.documentElement.clientHeight;
} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
//IE 4 compatible
myWidth = document.body.clientWidth;
myHeight = document.body.clientHeight;
}
return [myWidth, myHeight];
}
$(window).scroll(function()
{
if($(window).scrollTop() + getWindowSize()[1] == getDocHeight())
{
alert("bottom!");
}
});
You can try the following code,
$("#dashboard-scroll").scroll(function(){
var ele = document.getElementById('dashboard-scroll');
if(ele.scrollHeight - ele.scrollTop === ele.clientHeight){
console.log('at the bottom of the scroll');
}
});
scrollTop
can be fractional, while scrollHeight
and clientHeight
are not.
Try this for match condition if scroll to bottom end
if ($(this)[0].scrollHeight - $(this).scrollTop() ==
$(this).outerHeight()) {
//code for your custom logic
}
This gives accurate results, when checking on a scrollable element (i.e. not window
):
// `element` is a native JS HTMLElement
if ( element.scrollTop == (element.scrollHeight - element.offsetHeight) )
// Element scrolled to bottom
offsetHeight
should give the actual visible height of an element (including padding, margin, and scrollbars), and scrollHeight
is the entire height of an element including invisible (overflowed) areas.
jQuery
's .outerHeight()
should give similar result to JS's .offsetHeight
-- the documentation in MDN for offsetHeight
is unclear about its cross-browser support. To cover more options, this is more complete:
var offsetHeight = ( container.offsetHeight ? container.offsetHeight : $(container).outerHeight() );
if ( container.scrollTop == (container.scrollHeight - offsetHeight) ) {
// scrolled to bottom
}
Math.floor(element.scrollTop) === element.scrollHeight - element.offsetHeight
in case the scrollTop is a decimal it won't work without Math.floor()
i used this test to detect the scroll reached the bottom: event.target.scrollTop === event.target.scrollHeight - event.target.offsetHeight
Here's my two cents as the accepted answer didn't work for me.
var documentAtBottom = (document.documentElement.scrollTop + window.innerHeight) >= document.documentElement.scrollHeight;
Google Chrome gives the full height of the page if you call $(window).height()
Instead, use window.innerHeight
to retrieve the height of your window. Necessary check should be:
if($(window).scrollTop() + window.innerHeight > $(document).height() - 50) {
console.log("reached bottom!");
}
Many other solutions doesn't work for me Because on scroll to bottom my div was triggering the alert 2 times and when moving up it was also trigerring upto a few pixels so The solution is:
$('#your-div').on('resize scroll', function()
{
if ($(this).scrollTop() +
$(this).innerHeight() >=
$(this)[0].scrollHeight + 10) {
alert('reached bottom!');
}
});
Safari can scroll past the bottom of the page which was causing a bug in our application. Solve this using >=
instead of ===
.
container.scrollTop >= container.scrollHeight - container.clientHeight
Here is the most simple way to do it:
const handleScroll = () => {
if (window.innerHeight + window.pageYOffset >= document.body.offsetHeight) {
console.log('scrolled to the bottom')
}}
window.addEventListener('scroll', handleScroll)
element
. This is a top answer here for bottom of body
and with JS not jQuery.
(2021) Lots of answers here involve a ref to an element
, but if you only care about the whole page, just use:
function isBottom() {
const { scrollHeight, scrollTop, clientHeight } = document.documentElement;
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
return distanceFromBottom < 20; // adjust the number 20 yourself
}
Let me show approch without JQuery. Simple JS function:
function isVisible(elem) {
var coords = elem.getBoundingClientRect();
var topVisible = coords.top > 0 && coords.top < 0;
var bottomVisible = coords.bottom < shift && coords.bottom > 0;
return topVisible || bottomVisible;
}
Short example how to use it:
var img = document.getElementById("pic1");
if (isVisible(img)) { img.style.opacity = "1.00"; }
I used @ddanone answear and added Ajax call.
$('#mydiv').on('scroll', function(){
function infiniScroll(this);
});
function infiniScroll(mydiv){
console.log($(mydiv).scrollTop()+' + '+ $(mydiv).height()+' = '+ ($(mydiv).scrollTop() + $(mydiv).height()) +' _ '+ $(mydiv)[0].scrollHeight );
if($(mydiv).scrollTop() + $(mydiv).height() == $(mydiv)[0].scrollHeight){
console.log('bottom found');
if(!$.active){ //if there is no ajax call active ( last ajax call waiting for results ) do again my ajax call
myAjaxCall();
}
}
}
To stop repeated alert of Nick's answer
ScrollActivate();
function ScrollActivate() {
$(window).on("scroll", function () {
if ($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
$(window).off("scroll");
alert("near bottom!");
}
});
}
Success story sharing
console.log()
instead of alert it sometimes repeats the command.