ChatGPT解决这个技术问题 Extra ChatGPT

Check if a user has scrolled to the bottom (not just the window, but any element) [duplicate]

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.

That's funny, I'm trying to figure out which function is being called when I scroll to the bottom, so I can block this infuriating "feature".
For a React.js solution, this link might help: stackoverflow.com/a/53158893/4265546
if (window.innerHeight - elem.getBoundingClientRect().bottom === 0) This condition works perfect if you want to know that element is at the bottom or not
Answers here are wrong. The true correct answer is here: stackoverflow.com/questions/55419779

N
Nick Craver

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.


As usual you get there before me. Anyway, to the OP, if you have a container of posts, use it's ID instead of "window", also, you might want to change the last .height() to scrollHeight
Firefox calls alert("bottom!"); many times (works fine with chrome and safari)
it's kinda buggy, if you do a console.log() instead of alert it sometimes repeats the command.
@KevinVella Vella good combination is to use this with _.debounce() in underscore utility to prevent it firing until user has stopped scrolling - underscorejs.org/#debounce -
@NickCraver is there any pure js implementation available?
F
Félix Adriyel Gagnon-Grenier

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.


Note that in some situations this may not be the case. Firefox 47 reports a textarea clientHeight as 239 while the above equation gives 253 when scrolled to the bottom. Maybe padding/border or something is affecting it? Either way, adding some wiggle room wouldn't hurt, like 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).
I would add that it's possible for it to return false negatives as one side could return a small decimal while the other side returns a whole number (eg. 200.181819304947 and 200). I added a Math.floor() to help deal with that but I don't know how reliable this will be.
For me, this doesn't work cross browser. I get completely different results on chrome compared to firefox.
I tried this on the element, and this is firing when the browser is scrolled to the TOP, not the bottom.
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.
i
iwasrobbed

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!");
       }
   });

He has updated with a slightly compressed version 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 ); }
that alerts me when I reach the top, not the bottom (Chrome).
It might be better to use 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.
This answer only works for top level window scroll, not for any other scroll container.
G
George Filippakos

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);
}

It should probably be pointed out that this requires Underscore.js: underscorejs.org. It's a very good point, though. The scroll event should pretty much always be throttled to avoid serious performance issues.
Maybe it's just me and it could use some benchmarking, but I don't like the idea of invoking clearTimeout and setTimeout on every scroll event. I would issue 1 setTimeout and add some guards in that handler. It's probably just a micro-optimization (if at all).
This is a really neat addition! I can think of one further improvement . Currently the function that checks the height wont run if the user continues to scroll. If this is a problem (e.g. you want to trigger the function when the user scrolled half way down regardless of if they keep scrolling) you can easily make sure that the function actually gets called while still prohibiting multiple calls during the chosen interval. Here is a gist to show it. gist.github.com/datacarl/7029558
What? Invoke a load of machinery to prevent a simple calculation? I don't see how that is more efficient at all
L
Liam

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


window.innerHeight made more sense for what i was trying to accomplish thanks!
How to get remaining height after scroll?
This only works for top-level window scroll, but not for any other scroll area in any element.
F
Frederik Witte

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.


it used to work for me , but don't know due to browser update or something , it is getting missed by few decimals like value1 : 1200.1111450195312 value2 : 1200
This does not work all the time because scrollTop is not rounded, so it will be false even if the user scrolled to the bottom in many cases.
would it work if elm.clientHeight was put throught Math.round()?
C
Community

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


This only works for top level window scroll, but not scroll in any other element.
P
Peter Hall

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.


Best answer for Vanilla js, No jQuery solution. Use var docElement = document.documentElement; var winElement = window
This in 2019. Instead of winElement.pageYOffset you can also use winElement.scrollY.
This only works for top level window scroll, but not scroll in any other element.
V
Vasyl Gutnyk
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.


I did the following and worked flawlessly. Thanks. var shouldScroll = (elem.scrollHeight - elem.scrollTop - elem.clientHeight) == 0;
This does not work half the time because scrollTop is not rounded (can be fractional) so sometimes the value is not 0 even when scrolled to the bottom.
@trusktr will check it and update my answer
d
ddanone

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');
        }
    });

Tried yo use your solution, it works fine but in the cases when element has padding - it doesn't work because it calculates pure height of element, without including of padding, to solve this I changed this $(this).scrollTop() + $(this).height() == $(this)[0].scrollHeight => $(this).scrollTop() + $(this).outerHeight(true) >= $(this)[0].scrollHeight
This can not work all the time because scrollTop is not rounded (it can be fractional) while scrollHeight is rounded. This will have false positives.
T
TylerH

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


I get the following error: Expected 3 arguments, but got 2. An argument 'c' was not provided.
window.innerHeight - elem.getBoundingClientRect().bottom
This only works for top level window scroll, but not scroll in any other element.
@trusktr And voted down?
F
Fay Boisam

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; }

content
content
content
content
content
content
content
content
content
content
content


This will have false positives. This doesn't work all the time because scrollTop is not rounded (it can be fractional) while scrollHeight is, so sometimes the calculation will be > 0.
S
StefansArya

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; }

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10


The problem with Intersection Observer is that if the starting window is relatively small ( say basic layout before fetching images without scrollbar) observer callback is triggered immediately. Scroll listener actually waits for a person to scroll. And scrollbar won't appear before first batch of images and etc. is fetched. So Intersection Observer is not all in one solution.
@Aleks You can call the .observe(...) on an event listener like when the user hovering/touching your scroll container so it won't trigger immediately.
This won't work if the last element is taller than the scroll area, for exxample.
this does not work if you add some element above or bellow
S
Sarah

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 });

F
Florin Andrei

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");
}

A
Abhay Singh

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);

h
hayj

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!");
    }
});

S
Shahrukh Anwar

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');
    }
});

This doesn't work all the time. scrollTop can be fractional, while scrollHeight and clientHeight are not.
J
Jake Freeman

Try this for match condition if scroll to bottom end

if ($(this)[0].scrollHeight - $(this).scrollTop() == 
    $(this).outerHeight()) {

    //code for your custom logic

}

Y
Yuval A.

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
}


try Math.floor(element.scrollTop) === element.scrollHeight - element.offsetHeight in case the scrollTop is a decimal it won't work without Math.floor()
D
Dharman

i used this test to detect the scroll reached the bottom: event.target.scrollTop === event.target.scrollHeight - event.target.offsetHeight


a
alseether

Here's my two cents as the accepted answer didn't work for me.

var documentAtBottom = (document.documentElement.scrollTop + window.innerHeight) >= document.documentElement.scrollHeight;

a
alierdogan7

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!");
}

M
MR_AMDEV

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!');
            }
        });

S
Scott Wager

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

D
Dremiq

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)

Thank yoU! Lots of talk here about being notified about scroll to the bottom of an element. This is a top answer here for bottom of body and with JS not jQuery.
Z
ZYinMD

(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
}

A
Alexei Zababurin

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";  }

This doesn't answer the question. The question was how to detect that the users has scrolled the window all the way to the bottom. Your code is checking if a particular element is in view.
H
Henrique C.

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();
    }
}

}


A
Arun Prasad E S

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!");
        }
    });
}