iScroll Scrolling Past Bottom?

13,132

Solution 1

I think Andrew was on the right track with regards to setting the height of the #wrapper div. As he pointed out that,

that.maxScrollY = that.wrapperH - that.scrollerH;

Normally, this would work. But now that you've changed your #content to position: fixed, the wrapper element is no longer "wrapping" your content, thus that.wrapperH has a value of 0, things break.

Disclaimer: I did not go through the entire script so I may be wrong here

When manually setting a height to #wrapper, say 500px, it becomes,

that.maxScrollY = 500 - that.scrollerH;

The folly here is that when there's a lot of content and the window is small, that.scrollerH is relatively close in value to 500, say 700px. The difference of the two would be 200px, so you can only scroll 200 pixels, thus giving the appearance that it is frozen. This boils down to how you set that maxScrollY value.

Solution (for Chrome browser at least):

Since #wrapper effectively contains no content, we cannot use it in the calculations. Now we are left with the only thing that we can reliably get these dimensions from, #content. In this particular case, it appears that using the content element's scrollHeight yield what we want. This is most likely the one that has the expected behavior,

that.maxScrollY = that.scrollerH - that.scroller.scrollHeight;

scrollerH is the offsetHeight, which is roughly the height of what you see in the window. scroller.scrollHeight is the height that's considered scrollable. When the content does not exceed the length of the page, they are roughly equivalent to one another. That means no scroll. When there are a lot of content, the difference of these two values is the amount of scroll you need.

There is still a minor bug, and this looks like it's already there. When you have a lot of content, the last few elements are covered up by the bar when scrolled to the bottom. To fix this, you can set an offset such as,

that.maxScrollY = that.scrollerH - that.scroller.scrollHeight - 75;

The number 75 arbitrary. It's probably best if it's the height of the bar itself with 2 or 3 pixels for a bit of padding. Good luck!

Edit:

I forgot to mention last night, but here are the two sample pages that I used in trying to debug this problem.

Long page
Short page

Solution 2

This may be a CSS issue. In your stylesheet (mobile.css line 22), try removing position:fixed from #content.

That should allow the document to scroll normally (vertical scrollbar on a computer, "slideable" on a mobile browser).

Elements with position:fixed exit the normal flow of the document, their positioning is relative to the browser window. This is probably why you're having issues with scrolling. Fixed positioning is generally for elements which should always remain in the same place, even when the page is scrolled (ie. a notification bar "pinned" at the top of a page).

Solution 3

Your wrapper div seems to have a height of 0. So all the calculations are negative, setting it's height to the window height will correct the scroll issue. When I manually set the wrappers height via firebug and chromes debug bar the scroll functions as it should.

You #content div seems to have its size change on resize, probably a better idea to have the #wrapper div have its size change and then have #content inherit the size.

[Edit] You don't believe me so codez, From iscroll-lite.js

refresh: function () {
  var that = this,
      offset;
  that.wrapperW = that.wrapper.clientWidth;
  that.wrapperH = that.wrapper.clientHeight;
  that.scrollerW = that.scroller.offsetWidth;
  that.scrollerH = that.scroller.offsetHeight;
  that.maxScrollX = that.wrapperW - that.scrollerW;
  that.maxScrollY = that.wrapperH - that.scrollerH; 

In your page that translates to,

that.wrapperH = 0;
that.maxScrollY = -that.scrollerH

When a scroll finishes, this code gets called.

var that = this,

resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x,
resetY = that.y >= 0 || that.maxScrollY > 0 ? 0 : that.y < that.maxScrollY ? that.maxScrollY : that.y;
...
that.scrollTo(resetX, resetY, time || 0);

See that that.maxScrollY > 0 ? ? If maxScrollY is negative then scrolling up will never bounce back.

Solution 4

No definite solution, but more a direction I'd go for: #wrapper and #content's overflow:hidden paired #content's postion:fixed and seem to be the cause of the issue.

If position: fixed is removed from #content, scrolling is possible but the "blank" divs are wrongly layered (tested in Firefox 5).

Share:
13,132
Ry-
Author by

Ry-

If you don’t use code formatting for emphasis, we can probably be friends.

Updated on June 04, 2022

Comments

  • Ry-
    Ry- almost 2 years

    You can easily see the problem on the first page here: http://m.vancouverislandlife.com/

    Scroll down (slide up) and allow the content to leave the page, and it doesn't bounce back and is lost forever. However, on pages whose content does overflow the page and is therefore supposed to be scrollable, the scrolling works correctly (see Accomodations > b&b's and scroll down for an example of this).

    I noticed that on my computer, the scrolling on the first page is always stuck at -899px. I can't find anybody else who's experienced this problem and no matter what I try, I just can't fix it! Help!

    (It's not exactly urgent, however, as the target audience of iPhones and iPod Touches aren't affected by this since they have so little screen room.)

    Okay, new problem. To solve the iScroll issue, I just created a custom script. However, it's not working correctly on the actual device. On desktop browsers, it works just fine. On mobile, it occasionally jumps back to the top and won't recognize some touches. This is probably because of the way I cancelled the default event and had to resort to a bit of a hack. How can I fix this? (Yup - simple problem for a +500 bounty. Not bad, huh?)

    Here's the script, and the website is at the usual place:

    function Scroller(content) {
        function range(variable, min, max) {
            if(variable < min) return min > max ? max : min;
            if(variable > max) return max;
            return variable;
        }
    
        function getFirstElementChild(element) {
            element = element.firstChild;
    
            while(element && element.nodeType !== 1) {
                element = element.nextSibling;
            }
    
            return element;
        }
    
        var isScrolling = false;
        var mouseY = 0;
        var cScroll = 0;
        var momentum = 0;
        if("createTouch" in document) {
            content.addEventListener('touchstart', function(evt) {
                isScrolling = true;
                mouseY = evt.pageY;
                evt.preventDefault();
            }, false);
            content.addEventListener('touchmove', function(evt) {
                if(isScrolling) {
                    evt = evt.touches[0];
    
                    var dY = evt.pageY - mouseY;
                    mouseY = evt.pageY;
                    cScroll += dY;
                    momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
    
                    var firstElementChild = getFirstElementChild(content);
    
                    content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
                }
            }, false);
            window.addEventListener('touchend', function(evt) {
                isScrolling = false;
            }, false);
        } else {
            content.addEventListener('mousedown', function(evt) {
                isScrolling = true;
                mouseY = evt.pageY;
            }, false);
            content.addEventListener('mousemove', function(evt) {
                if(isScrolling) {
                    var dY = evt.pageY - mouseY;
                    mouseY = evt.pageY;
                    cScroll += dY;
                    momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
    
                    var firstElementChild = getFirstElementChild(content);
    
                    content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
                }
            }, false);
            window.addEventListener('mouseup', function(evt) {
                isScrolling = false;
            }, false);
        }
    
        function scrollToTop() {
            cScroll = 0;
            content.style.WebkitTransform = '';
        }
    
        function performAnimations() {
            if(!isScrolling) {
                var firstElementChild = getFirstElementChild(content);
                cScroll = range(cScroll + momentum, -(firstElementChild.scrollHeight - content.offsetHeight), 0);
                content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
                momentum *= Scroller.FRICTION;
            }
        }
    
        return {
            scrollToTop: scrollToTop,
            animationId: setInterval(performAnimations, 33)
        }
    }
    
    Scroller.MAX_MOMENTUM = 100;
    Scroller.ACCELERATION = 1;
    Scroller.FRICTION = 0.8;