Search code examples
javascriptjqueryhtmllag

Faster alternative to $(window).scroll?


I've noticed that the jQuery scroll bind of $(window).scroll tends to lag down pages a significant amount. For example, I have elements on my page change styles when I scroll past them using the following script:

$(window).scroll(function() {
     var bottom_of_window = $(window).scrollTop() + $(window).height();
     $('.ElementsToBeChanged').each(function() { 
            var bottom_of_object = $(this).offset().top + $(this).outerHeight();
            if (bottom_of_window > bottom_of_object) {
                //Add my styles
            }
        });

});

It understandably makes the website laggy, since it constantly runs on scroll, but I haven't come across any alternative for triggering events when objects are scrolled past. These kinds of websites that trigger events on scroll seem pretty common; What do they use to bypass this lag?


Solution

  • What slows down your performance is not the scroll function itself but the fact that you're doing a lot of calculations per each item on every scroll event.

    But despite that I wasn't able to see any drop in performance with 20 elements on screen.

    Try to cache your window object as variable [ex: win = $(window)] and use el from the each function instead of this.

    Instead of changing your css directly using jQuery, try adding/removing classes.

    Don't use denounce as others have mentioned as it will not fire until you stop scrolling. What you're looking for is called throttling.

    Here's what I came up with and it works well with 20 elements on my PC:

    var win = $(window);
    
    win.scroll(function() {
         var bottom_of_window = win.scrollTop() + win.height();
         $('.ElementsToBeChanged').each(function(index,el) { 
                var bottom_of_object = $(el).offset().top + $(el).outerHeight();
                if (bottom_of_window > bottom_of_object) {
                    $(el).addClass('red');
                }else{
                		$(el).removeClass('red');
                }
            });
    
    });
    .ElementsToBeChanged{
      margin:100px;
      height:200px;
      border:1px solid black;
      transition:2s;
    }
    
    .ElementsToBeChanged.red{
      background:red;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>
    <div class="ElementsToBeChanged">.ElementsToBeChanged</div>

    Lastly, just use a plugin such as Skrollr.js or ScrollMagic