0

I'm trying to make responsive website but i'm struggling with how to disable the rubber band effect on iOS devices. The reason for disabling it is due to wanting to make it more like an app and not a website.

I've found some code but it's pretty old an no one seems to be around to answer it anymore so I have put what i've got in a fiddle and i'm hoping someone can help.

What I am after is a page that allows the user to scroll down then stop once it reaches the top or bottom, no rubber band.

Here's the code i found

(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);
Dan
  • 1,565
  • 3
  • 23
  • 43

1 Answers1

0

You can also do this on the HTML+JS side, provided that the HTML document is not taller than the WebView viewport (aka window.height in JS). You do this by calling preventDefault on the 'touchmove' event at the appropriate time (namely when the element and all its parents have nothing to scroll in the direction the user began moving their finger).

I will show you actual code to do it, without using jQuery ... but you'll have to implement Q.addEventListener and Q.removeEventListener yourself (or use jQuery).

var Q = {
    preventTouchScrolling: function () {
        Q.addEventListener(window, 'touchmove', _touchScrollingHandler);
    },

    restoreTouchScrolling: function () {
        Q.removeEventListener(window, 'touchmove', _touchScrollingHandler);
    },
    Pointer: {
        /**
         * Consistently prevents the default behavior of an event across browsers
         * @static
         * @method preventDefault
         * @param {Event} e Some mouse or touch event from the DOM
         * @return {Boolean} Whether the preventDefault succeeded
         */
        preventDefault: function (e) {
            if (('cancelable' in e) && !e.cancelable) {
                return false;
            }
            e.preventDefault ? e.preventDefault() : e.returnValue = false;
            return true;
        },

        /**
         * Returns the document's scroll top in pixels, consistently across browsers
         * @static
         * @method scrollTop
         * @return {Number}
         */
        scrollTop: function () {
            return window.pageYOffset || document.documentElement.scrollTop || (document.body && document.body.scrollTop);
        },

        /**
         * Returns the y coordinate of an event relative to the document
         * @static
         * @method getY
         * @param {Event} e Some mouse or touch event from the DOM
         * @return {Number}
         */
        getY: function(e) {
            var oe = e.originalEvent || e;
            oe = (oe.touches && oe.touches.length)
                ? oe.touches[0]
                : (oe.changedTouches && oe.changedTouches.length
                    ? oe.changedTouches[0]
                    : oe
                );
            return Math.max(0, ('pageY' in oe) ? oe.pageY : oe.clientY + Q.Pointer.scrollTop());
        },
    }
};

function _touchScrollingHandler(event) {
    var p = event.target;
    var pos;
    var scrollable = null;
    do {
        if (!p.computedStyle) {
            continue;
        }
        var overflow = p.computedStyle().overflow;
        var hiddenHeight = p.scrollHeight - p.offsetHeight;
        var s = (['hidden', 'visible'].indexOf(overflow) < 0);
        if ((s || p.tagName === 'HTML') && hiddenHeight > 0) {
            if ((Q.Pointer.movement.positions.length == 1)
            && (pos = Q.Pointer.movement.positions[0])) {
                var sy = Q.Pointer.getY(event)
                    + Q.Pointer.scrollTop();
                if ((sy > pos.y && p.scrollTop == 0)
                || (sy < pos.y && p.scrollTop >= hiddenHeight)) {
                    continue;
                }
            }
            scrollable = p;
            break;
        }
    } while (p = p.parentNode);
    if (!scrollable) {
        Q.Pointer.preventDefault(event);
    }
}
Gregory Magarshak
  • 1,883
  • 2
  • 25
  • 35