Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
537 views
in Technique[技术] by (71.8m points)

jquery - How to disable rubber band in iOS web apps?

This:

$('body').on('touchmove', function(e) { e.preventDefault(); });

Works, but will disable scrolling throughout the whole page, which is far from ideal.

This:

$('*').on('touchstart', function(e){
    var element = $(this).get(0);

    if ( element.scrollTop <= 0 )                                           element.scrollTop = 1;
    if ( element.scrollTop + element.offsetHeight >= element.scrollHeight ) element.scrollTop = element.scrollHeight - element.offsetHeight - 1;
});

Works on pages that have a scrolling area. However when there is nothing to scroll it will again show the rubber-band.

So my question:

How can you disable the rubber band effect and still keep the -webkit-overflow-scrolling areas scrollable?

[Update]

Best Solution

Disable scrolling on all non-scrollable elements such as a tab bar or a navigation bar.

anElement.addEventListener('touchmove', function( event ){ event.preventDefault() };

Attach a scroll handler to the scrollable elements such as the main content.

anElement.addEventListener('touchstart', function( event ){
        if( this.scrollTop === 0 ) {
            this.scrollTop += 1;
        } else if( this.scrollTop + this.offsetHeight >= this.scrollHeight ) {
            this.scrollTop -= 1;
        }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Ran into the same issue recently with a SPA where the <body> rubber-banding was detracting from the experience, but I needed scrolling in sub-areas. Many thanks to dSquared's suggestions, as Method 1 worked best for me. Here is my small expansion of his suggestion that I implemented in a project for work that looks all the way up the tree to find any elements (not just divs) that have a .scroll class on it:

// Prevent rubber-banding of the body, but allow for scrolling elements
$('body').on('touchmove', function (e) {
    var searchTerms = '.scroll, .scroll-y, .scroll-x',
        $target = $(e.target),
        parents = $target.parents(searchTerms);

    if (parents.length || $target.hasClass(searchTerms)) {
        // ignore as we want the scroll to happen
        // (This is where we may need to check if at limit)
    } else {
        e.preventDefault();
    }
});

And here is what the CSS looks like:

body {
    height: 100%;
    overflow: hidden;
}
.scroll, .scroll-y, .scroll-x {
    -webkit-overflow-scrolling: touch;
}
.scroll > *, .scroll-y > *, .scroll-x > * {
    -webkit-transform : translateZ(0);
}
.scroll { overflow: auto; }
.scroll-y { overflow-y: auto; }
.scroll-x { overflow-x: auto; }

You only need one library (jQuery or Zepto) and you get native scrolling with momentum and no rubber-banding on the body. Also, I've added the translateZ to fix some issues I've had with elements disappearing during scrolling and it can be used to GPU accelerate your elements.

BUT (and this is a big but), as dSquared points out, the whole page rubber-bands when the scroll element is at its limit and attempted to scroll further. Personally, I consider this a failure so I'm continuing to work on it, just wanted to pitch in on trying to figure this out. Adding a check along the lines of the OP's code might be the answer, but I haven't tried it.

UPDATE (10/7/12):

After lots of work, I've gotten the following code working perfectly in iOS6 (haven't tested in anything else). No rubber-banding on the body, no more issues when at the limit of the scroll area, and it has native scrolling performance throughout. It's obviously a lot more code that originally, but I think this will give the behavior closest to the OP's goals.

(function registerScrolling($) {
    var prevTouchPosition = {},
        scrollYClass = 'scroll-y',
        scrollXClass = 'scroll-x',
        searchTerms = '.' + scrollYClass + ', .' + scrollXClass;

    $('body').on('touchstart', function (e) {
        var $scroll = $(e.target).closest(searchTerms),
            targetTouch = e.originalEvent.targetTouches[0];

        // Store previous touch position if within a scroll element
        prevTouchPosition = $scroll.length ? { x: targetTouch.pageX, y: targetTouch.pageY } : {};
    });

$('body').on('touchmove', function (e) {
    var $scroll = $(e.target).closest(searchTerms),
        targetTouch = e.originalEvent.targetTouches[0];

    if (prevTouchPosition && $scroll.length) {
        // Set move helper and update previous touch position
        var move = {
            x: targetTouch.pageX - prevTouchPosition.x,
            y: targetTouch.pageY - prevTouchPosition.y
        };
        prevTouchPosition = { x: targetTouch.pageX, y: targetTouch.pageY };

        // Check for scroll-y or scroll-x classes
        if ($scroll.hasClass(scrollYClass)) {
            var scrollHeight = $scroll[0].scrollHeight,
                outerHeight = $scroll.outerHeight(),

                atUpperLimit = ($scroll.scrollTop() === 0),
                atLowerLimit = (scrollHeight - $scroll.scrollTop() === outerHeight);

            if (scrollHeight > outerHeight) {
                // If at either limit move 1px away to allow normal scroll behavior on future moves,
                // but stop propagation on this move to remove limit behavior bubbling up to body
                if (move.y > 0 && atUpperLimit) {
                    $scroll.scrollTop(1);
                    e.stopPropagation();
                } else if (move.y < 0 && atLowerLimit) {
                    $scroll.scrollTop($scroll.scrollTop() - 1);
                    e.stopPropagation();
                }

                // If only moving right or left, prevent bad scroll.
                if(Math.abs(move.x) > 0 && Math.abs(move.y) < 3){
                  e.preventDefault()
                }

                // Normal scrolling behavior passes through
            } else {
                // No scrolling / adjustment when there is nothing to scroll
                e.preventDefault();
            }
        } else if ($scroll.hasClass(scrollXClass)) {
            var scrollWidth = $scroll[0].scrollWidth,
                outerWidth = $scroll.outerWidth(),

                atLeftLimit = $scroll.scrollLeft() === 0,
                atRightLimit = scrollWidth - $scroll.scrollLeft() === outerWidth;

            if (scrollWidth > outerWidth) {
                if (move.x > 0 && atLeftLimit) {
                    $scroll.scrollLeft(1);
                    e.stopPropagation();
                } else if (move.x < 0 && atRightLimit) {
                    $scroll.scrollLeft($scroll.scrollLeft() - 1);
                    e.stopPropagation();
                }
                // If only moving up or down, prevent bad scroll.
                if(Math.abs(move.y) > 0 && Math.abs(move.x) < 3){
                  e.preventDefault();
                }

                // Normal scrolling behavior passes through
            } else {
                // No scrolling / adjustment when there is nothing to scroll
                e.preventDefault();
            }
        }
    } else {
        // Prevent scrolling on non-scrolling elements
        e.preventDefault();
    }
});
})(jQuery);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...