239

I have a div box (called flux) with a variable amount of content inside. This divbox has overflow set to auto.

Now, what I am trying to do, is, when the use scroll to the bottom of this DIV-box, load more content into the page, I know how to do this (load the content) but I don't know how to detect when the user has scrolled to the bottom of the div tag? If I wanted to do it for the entire page, I'd take .scrollTop and subtract that from .height.

But I can't seem to do that here?

I've tried taking .scrollTop from flux, and then wrapping all the content inside a div called inner, but if I take the innerHeight of flux it returns 564px (the div is set to 500 as height) and the height of 'innner' it returns 1064, and the scrolltop, when at the bottom of the div says 564.

What can I do?

gvlasov
  • 18,638
  • 21
  • 74
  • 110
Olive
  • 3,516
  • 7
  • 24
  • 32

16 Answers16

463

There are some properties/methods you can use:

$().scrollTop()//how much has been scrolled
$().innerHeight()// inner height of the element
DOMElement.scrollHeight//height of the content of the element

So you can take the sum of the first two properties, and when it equals to the last property, you've reached the end:

jQuery(function($) {
    $('#flux').on('scroll', function() {
        if($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
            alert('end reached');
        }
    })
});

http://jsfiddle.net/doktormolle/w7X9N/

Edit: I've updated 'bind' to 'on' as per:

As of jQuery 1.7, the .on() method is the preferred method for attaching event handlers to a document.

Dr.Molle
  • 116,463
  • 16
  • 195
  • 201
  • Thanks, works like a charm. Why do we need to wrap bind into jQuery(function($){ ... }) call? – Mark Vital Oct 21 '12 at 01:41
  • 2
    It will do the binding when the DOM is ready to be sure that the #flux-element is already available. – Dr.Molle Oct 21 '12 at 01:58
  • using this I built an Infinite Scroll plugin for jQuery : https://github.com/grnadav/jqueryinfinitescroll – Nadav Mar 02 '13 at 14:37
  • 1
    you should update you answer using ON instead of bind, bind is deprecated in favor of On – ncubica Apr 18 '13 at 15:21
  • I'm no able and willing to keep all my answers at SO up to date, at the time I gave this answer `$.on()` wasn't available in jQuery(1.6.1). Feel free to suggest an edit, thanks anyway. – Dr.Molle Apr 18 '13 at 15:49
  • 41
    Note: When a user is using zoom levels on their browser, this may not work. Zoom causes `innerHeight()` to report decimals. In the case of a user zooming out, the sum of `scrollTop()` and `innerHeight()` will always be at least a fraction short of `scrollHeight`. I ended up adding a buffer zone of 20px. The resulting conditional was `if(el.scrollTop() + el.innerHeight() >= el[0].scrollHeight - 20)` where `el` equals `$(this)`. – Nick Aug 13 '13 at 20:53
  • @Nick, excellent observation on zoom level causing an issue. In our application Chrome at 125% worked fine, but Chrome at %150 had a problem. We'll try out adding a buffer as you suggested. – Kirk Liemohn Sep 10 '14 at 17:17
  • @Nick, using the buffer will cause event to fire more than once if you scroll slowly (e.g. by grabbing and moving scroll handle with mouse) – Dziamid May 13 '15 at 16:50
  • @Dziamid Off the top of my head, you can probably set a flag when you hit the bottom and check for that flag each time. Disable the flag when the user scrolls back up. Something like this: `if(originalConditionalHere && !bottomReached){ bottomReached = true; ... }` – Nick May 13 '15 at 18:35
  • Especially when scrolling by dragging the scrollbar with the mouse, I often get multiple `alert('end reached');` calls triggered. I suspect unintentional mouse movements at the end of the scroll are making additional calls to the scroll event handler. Any ideas, anyone how this event can be "throttled" in this situation? Perhaps using timeout? – Ifedi Okonkwo Oct 31 '16 at 07:56
  • Thanks for the comments explaining what each method/property does/is. – tedi Dec 15 '16 at 13:15
  • 1
    This is the best answer ever that worked very well for me. Thanks – Ashish Yadav Feb 02 '17 at 07:49
  • 2
    It's not working anymore, Can you please confirm that why it's not working, Thanks – Umair Shah Jun 26 '17 at 23:08
  • 1
    because of what @Nick said, I changed this line: ` if($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {` to be ` if(Math.ceil($(this).scrollTop() + $(this).innerHeight()) >= $(this)[0].scrollHeight) {` – Alon Gouldman Apr 30 '19 at 12:04
  • fyi... `$(this)[0]` === `this` – pstanton Oct 11 '20 at 09:50
  • Not working on samll screens – Techno Nov 16 '22 at 11:36
60

Though the question was asked 5.5 years ago, still it is more than relevant in today's UI/UX context. And I would like to add my two cents.

var element = document.getElementById('flux');
if (element.scrollHeight - element.scrollTop === element.clientHeight)
    {
        // element is at the end of its scroll, load more content
    }

Source: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Determine_if_an_element_has_been_totally_scrolled

Some elements won't allow you to scroll the full height of the element. In those cases you can use:

var element = docuement.getElementById('flux');
if (element.offsetHeight + element.scrollTop === element.scrollHeight) {
  // element is at the end of its scroll, load more content
}
Thinking
  • 747
  • 5
  • 11
59

I found a solution that when you scroll your window and end of a div shown from bottom gives you an alert.

$(window).bind('scroll', function() {
    if($(window).scrollTop() >= $('.posts').offset().top + $('.posts').outerHeight() - window.innerHeight) {
        alert('end reached');
    }
});

In this example if you scroll down when div (.posts) finish its give you an alert.

Codler
  • 10,951
  • 6
  • 52
  • 65
ehsan Aghaei
  • 599
  • 4
  • 3
  • thank you for this but how can I make the div animate when it reach the bottom of that div? also when you scroll up detect the top of that div then animate also. Just like here on this website http://www.lazada.com.ph/apple/ the sidebar behavior Hope you can help me :) – Alyssa Reyes May 05 '16 at 19:37
  • It works like a magic but the problem it that the alert is executed two times so if you have a call of a function inside it will be executed two times – Afaf Feb 02 '17 at 11:51
  • 1
    @1616 I have a variable `var running = false` declared before this. Inside the conditional, I check `if(!running) { running = true; // and continue logic }` This way it's only called once! When the AJAX completes, set `running = false;` – Jacob Raccuia Feb 09 '18 at 04:20
  • 2
    It's about window, but question is about div. – Alexander Volkov Apr 04 '19 at 18:16
10

Just an additional note here as I found this when looking for a solution for a fixed div that I want to scroll. For my scenario I found that its preferable to take into account the padding on the div so I can hit the end exactly. So expanding on @Dr.Molle's answer I add the following

$('#flux').bind('scroll', function() {
    var scrollPosition = $(this).scrollTop() + $(this).outerHeight();
    var divTotalHeight = $(this)[0].scrollHeight 
                          + parseInt($(this).css('padding-top'), 10) 
                          + parseInt($(this).css('padding-bottom'), 10)
                          + parseInt($(this).css('border-top-width'), 10)
                          + parseInt($(this).css('border-bottom-width'), 10);

    if( scrollPosition == divTotalHeight )
    {
      alert('end reached');
    }
  });

That'll give you the precise location, including padding and borders

8

this worked for me though

$(window).scroll(function() {
  if ($(window).scrollTop() >= (($(document).height() - $(window).height()) - $('#divID').innerHeight())) {
    console.log('div reached');
  }
});
Ricardo Rivas
  • 620
  • 6
  • 8
5

If you are not using Math.round() function the solution suggested by Dr.Molle will not work in some cases when a browser window has a zoom.

For example $(this).scrollTop() + $(this).innerHeight() = 600

$(this)[0].scrollHeight yields = 599.99998

600 >= 599.99998 fails.

Here is the correct code:

jQuery(function($) {
    $('#flux').on('scroll', function() {
        if(Math.round($(this).scrollTop() + $(this).innerHeight(), 10) >= Math.round($(this)[0].scrollHeight, 10)) {
            alert('end reached');
        }
    })
});

You may also add some extra margin pixels if you do not need a strict condition

var margin = 4

jQuery(function($) {
    $('#flux').on('scroll', function() {
        if(Math.round($(this).scrollTop() + $(this).innerHeight(), 10) >= Math.round($(this)[0].scrollHeight, 10) - margin) {
            alert('end reached');
        }
    })
});
Community
  • 1
  • 1
4

In simple DOM usage you can check the condition

element.scrollTop + element.clientHeight == element.scrollHeight

if true then you have reached the end.

Louay Al-osh
  • 3,177
  • 15
  • 28
3

If you need to use this on a div that has overflow-y as hidden or scroll, something like this may be what you need.

if ($('#element').prop('scrollHeight') - $('#element').scrollTop() <= Math.ceil($('#element').height())) {
    at_bottom = true;
}

I found ceil was needed because prop scrollHeight seems to round, or perhaps some other reason causing this to be off by less than 1.

Goose
  • 4,764
  • 5
  • 45
  • 84
2

If anyone gets scrollHeight as undefined, then select elements' 1st subelement: mob_top_menu[0].scrollHeight

Starwave
  • 2,352
  • 2
  • 23
  • 30
2

Though this was asked almost 6 years, ago still hot topic in UX design, here is demo snippet if any newbie wanted to use

$(function() {

  /* this is only for demonstration purpose */
  var t = $('.posts').html(),
    c = 1,
    scroll_enabled = true;

  function load_ajax() {

    /* call ajax here... on success enable scroll  */
    $('.posts').append('<h4>' + (++c) + ' </h4>' + t);

    /*again enable loading on scroll... */
    scroll_enabled = true;

  }


  $(window).bind('scroll', function() {
    if (scroll_enabled) {

      /* if 90% scrolled */
    if($(window).scrollTop() >= ($('.posts').offset().top + $('.posts').outerHeight()-window.innerHeight)*0.9) {

        /* load ajax content */
        scroll_enabled = false;  
        load_ajax();
      }

    }

  });

});
h4 {
  color: red;
  font-size: 36px;
  background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class="posts">
  Lorem ipsum dolor sit amet  Consectetuer augue nibh lacus at <br> Pretium Donec felis dolor penatibus <br> Phasellus consequat Vivamus dui lacinia <br> Ornare nonummy laoreet lacus Donec <br> Ut ut libero Curabitur id <br> Dui pretium hendrerit
  sapien Pellentesque <br> Lorem ipsum dolor sit amet <br> Consectetuer augue nibh lacus at <br> Pretium Donec felis dolor penatibus <br> Phasellus consequat Vivamus dui lacinia <br> Ornare nonummy laoreet lacus Donec <br> Ut ut libero Curabitur id <br>  Dui pretium hendrerit sapien Pellentesque <br> Lorem ipsum dolor sit amet <br> Consectetuer augue nibh lacus at <br> Pretium Donec felis dolor penatibus <br> Phasellus consequat Vivamus dui lacinia <br> Ornare nonummy laoreet lacus Donec <br> Ut ut
  libero Curabitur id <br> Dui pretium hendrerit sapien Pellentesque <br> Lorem ipsum dolor sit amet <br> Consectetuer augue nibh lacus at <br> Pretium Donec felis dolor penatibus <br> Phasellus consequat Vivamus dui lacinia <br> Ornare nonummy laoreet
  lacus Donec <br> Ut ut libero Curabitur id <br> Dui pretium hendrerit sapien Pellentesque
</div>
Akshay Hegde
  • 16,536
  • 2
  • 22
  • 36
1

Here's another version.

The key code is function scroller() which takes input as the height of the div containing the scrolling section, using overflow:scroll. It approximates 5px from the top or 10px from the bottom as at the top or bottom. Otherwise it's too sensitive. It seems 10px is about the minimum. You'll see it adds 10 to the div height to get the bottom height. I assume 5px might work, I didn't test extensively. YMMV. scrollHeight returns the height of the inner scrolling area, not the displayed height of the div, which in this case is 400px.

<?php

$scrolling_area_height=400;
echo '
<script type="text/javascript">
          function scroller(ourheight) {
            var ourtop=document.getElementById(\'scrolling_area\').scrollTop;
            if (ourtop > (ourheight-\''.($scrolling_area_height+10).'\')) {
                alert(\'at the bottom; ourtop:\'+ourtop+\' ourheight:\'+ourheight);
            }
            if (ourtop <= (5)) {
                alert(\'Reached the top\');
            }
          }
</script>

<style type="text/css">
.scroller { 
            display:block;
            float:left;
            top:10px;
            left:10px;
            height:'.$scrolling_area_height.';
            border:1px solid red;
            width:200px;
            overflow:scroll; 
        }
</style>

$content="your content here";

<div id="scrolling_area" class="scroller">
onscroll="var ourheight=document.getElementById(\'scrolling_area\').scrollHeight;
        scroller(ourheight);"
        >'.$content.'
</div>';

?>
  • 3
    -1 because generating javascript+css code from php is bad practice (hard to maintain, no reusability, no website optimizations etc). Furthermore as pointed out by @Alexander Millar the 10px offset can be fixed by taking padding in to account. Anyone considering using this code should think twice... – Kapitein Witbaard Jan 29 '16 at 09:32
1

not sure if it is any help but this is how I do it.

I have an index panel that is larger that the window and I let it scroll until the end this index is reached. Then I fix it in position. The process is reversed once you scroll toward the top of the page.

Regards.

<style type="text/css">
    .fixed_Bot {    
            position: fixed;     
            bottom: 24px;    
        }     
</style>

<script type="text/javascript">
    $(document).ready(function () {
        var sidebarheight = $('#index').height();
        var windowheight = $(window).height();


        $(window).scroll(function () {
            var scrollTop = $(window).scrollTop();

            if (scrollTop >= sidebarheight - windowheight){
                $('#index').addClass('fixed_Bot');
            }
            else {
                $('#index').removeClass('fixed_Bot');
            }                   
        });
    });

</script>
1

Guys this is the solution to the zoom issue, it works with all zoom levels, in case you need it:

if ( Math.abs(elem.offset().top) + elem.height() + elem.offset().top >= elem.outerHeight() ) {
                    console.log("bottom");
                    // We're at the bottom!
                }
            });
        }
Liad Livnat
  • 7,435
  • 16
  • 60
  • 98
0
$(window).on("scroll", function() {
    //get height of the (browser) window aka viewport
    var scrollHeight = $(document).height();
    // get height of the document 
    var scrollPosition = $(window).height() + $(window).scrollTop();
    if ((scrollHeight - scrollPosition) / scrollHeight === 0) {
        // code to run when scroll to bottom of the page
    }
});

This is the code on github.

Tim Diekmann
  • 7,755
  • 11
  • 41
  • 69
aki_sud
  • 201
  • 2
  • 3
0

I have crafted this piece of code that worked for me to detect when I scroll to the end of an element!

let element = $('.element');

if ($(document).scrollTop() > element.offset().top + element.height()) {

     /// do something ///
}
ThisIsWilliam
  • 995
  • 8
  • 10
0

none of the answers worked for me, this one did:

$(window).on('scroll', function() {
    if ($(window).scrollTop() >= $('#flux').offset().top + $('#flux').outerHeight() - window.innerHeight) {
        alert('You reached the end of the DIV');
    }
});
eylay
  • 1,712
  • 4
  • 30
  • 54