Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery scroll event: how to determine amount scrolled (scroll delta) in pixels?

I have this event:

$(window).scroll(function(e){    
    console.log(e);
})

I want to know, how much I have scroll value in pixels, because I think, scroll value depends from window size and screen resolution.

Function parameter e does not contains this information.
I can store $(window).scrollTop() after every scroll and calculate difference, but can I do it differently?

like image 981
Andrey Vorobyev Avatar asked Nov 18 '14 09:11

Andrey Vorobyev


4 Answers

The "scroll value" does not depend on the window size or screen resolution. The "scroll value" is simply the number of pixels scrolled.

However, whether you are able to scroll at all, and the amount you can scroll is based on available real estate for the container and the dimensions of the content within the container (in this case the container is document.documentElement, or document.body for older browsers).

You are correct that the scroll event does not contain this information. It does not provide a delta property to indicate the number of pixels scrolled. This is true for the native scroll event and the jQuery scroll event. This seems like it would be a useful feature to have, similar to how mousewheel events provide properties for X and Y delta.

I do not know, and will not speculate upon, why the powers-that-be did not provide a delta property for scroll, but that is out of scope for this question (feel free to post a separate question about this).

The method you are using of storing scrollTop in a variable and comparing it to the current scrollTop is the best (and only) method I have found. However, you can simplify this a bit by extending jQuery to provide a new custom event, per this article: http://learn.jquery.com/events/event-extensions/

Here is an example extension I created that works with window / document scrolling. It is a custom event called scrolldelta that automatically tracks the X and Y delta (as scrollLeftDelta and scrollTopDelta, respectively). I have not tried it with other elements; leaving this as exercise for the reader. This works in currrent versions of Chrome and Firefox. It uses the trick for getting the sum of document.documentElement.scrollTop and document.body.scrollTop to handle the bug where Chrome updates body.scrollTop instead of documentElement.scrollTop (IE and FF update documentElement.scrollTop; see https://code.google.com/p/chromium/issues/detail?id=2891).

JSFiddle demo: http://jsfiddle.net/tew9zxc1/

Runnable Snippet (scroll down and click Run code snippet):

// custom 'scrolldelta' event extends 'scroll' event
jQuery.event.special.scrolldelta = {
    delegateType: "scroll",
    bindType: "scroll",
    handle: function (event) {
        var handleObj = event.handleObj;
        var targetData = jQuery.data(event.target);
        var ret = null;
        var elem = event.target;
        var isDoc = elem === document;
        var oldTop = targetData.top || 0;
        var oldLeft = targetData.left || 0;
        targetData.top = isDoc ? elem.documentElement.scrollTop + elem.body.scrollTop : elem.scrollTop;
        targetData.left = isDoc ? elem.documentElement.scrollLeft + elem.body.scrollLeft : elem.scrollLeft;
        event.scrollTopDelta = targetData.top - oldTop;
        event.scrollTop = targetData.top;
        event.scrollLeftDelta = targetData.left - oldLeft;
        event.scrollLeft = targetData.left;
        event.type = handleObj.origType;
        ret = handleObj.handler.apply(this, arguments);
        event.type = handleObj.type;
        return ret;
    }
};

// bind to custom 'scrolldelta' event
$(window).on('scrolldelta', function (e) {
    var top = e.scrollTop;
    var topDelta = e.scrollTopDelta;
    var left = e.scrollLeft;
    var leftDelta = e.scrollLeftDelta;
  // do stuff with the above info; for now just display it to user
    var feedbackText = 'scrollTop: ' + top.toString() + 'px (' + (topDelta >= 0 ? '+' : '') + topDelta.toString() + 'px), scrollLeft: ' + left.toString() + 'px (' + (leftDelta >= 0 ? '+' : '') + leftDelta.toString() + 'px)';
    document.getElementById('feedback').innerHTML = feedbackText;
});
#content {
    /* make window tall enough for vertical scroll */
    height: 2000px;
    /* make window wide enough for horizontal scroll */
    width: 2000px;
    /* visualization of scrollable content */
    background-color: blue;
}
#feedback {
    border:2px solid red;
    padding: 4px;
    color: black;
    position: fixed;
    top: 0;
    height: 20px;
    background-color: #fff;
    font-family:'Segoe UI', 'Arial';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='feedback'>scrollTop: 0px, scrollLeft: 0px</div>
<div id='content'></div>

Note that you may want debounce the event depending on what you are doing. You didn't provide very much context in your question, but if you give a better example of what you are actually using this info for we can provide a better answer. (Please show more of your code, and how you are using the "scroll value").

like image 64
nothingisnecessary Avatar answered Oct 02 '22 03:10

nothingisnecessary


To detemine how many pixels were scrolled you have to keep in mind that the scroll event gets fired almost every pixel that you move. The way to accomplish it is to save the previous scrolled value and compare that in a timeout. Like this:

var scrollValue = 0;
var scrollTimeout = false

$(window).scroll(function(event){
    /* Clear it so the function only triggers when scroll events have stopped firing*/
    clearTimeout(scrollTimeout);
    /* Set it so it fires after a second, but gets cleared after a new triggered event*/
    scrollTimeout = setTimeout(function(){
        var scrolled = $(document).scrollTop() - scrollValue;
        scrollValue = $(document).scrollTop();
        alert("The value scrolled was " + scrolled);
    }, 1000);
});

This way you will get the amount of scrolled a second after scrolling (this is adjustable but you have to keep in mind that the smooth scrolling that is so prevalent today has some run-out time and you dont want to trigger before a full stop).

like image 28
somethinghere Avatar answered Oct 02 '22 03:10

somethinghere


The other way to do this? Yes, possible, with jQuery Mobile
I do not appreciate this solution, because it is necessary to include heavy jQuery mobile. Solution:

var diff, top = 0;
$(document).on("scrollstart",function () {
  // event fired when scrolling is started
  top = $(window).scrollTop();
});
$(document).on("scrollstop",function () {
  // event fired when scrolling is stopped
  diff = Math.abs($(window).scrollTop() - top);
});
like image 30
iamawebgeek Avatar answered Oct 02 '22 03:10

iamawebgeek


To reduce the used processing power by adding a timer to a Jquery scroll method is probably not a great idea. The visual effect is indeed quite bad.

The whole web browsing experience could be made much better by hiding the scrolling element just when the scroll begins and making it slide in (at the right position) some time after. The scrolling even can be checked with a delay too. This solution works great.

$(document).ready(function() {

    var element = $('.movable_div'),
    originalY = element.offset().top;
    element.css('position', 'relative');

    $(window).on('scroll', function(event) {
        var scrollTop = $(window).scrollTop();
        element.hide();
        element.stop(false, false).animate({
            top: scrollTop < originalY
                    ? 0
                    : scrollTop - originalY + 35
        }, 2000,function(){element.slideDown(500,"swing");});
    });
});

Live demo here

like image 31
Fabrizio Avatar answered Oct 02 '22 03:10

Fabrizio