jQuery 如何在 iOS 网络应用程序中禁用橡皮筋?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10357844/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 09:10:55  来源:igfitidea点击:

How to disable rubber band in iOS web apps?

jqueryweb-applicationsrubber-band

提问by Mark

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-scrollingareas scrollable?

如何禁用橡皮筋效果并保持-webkit-overflow-scrolling区域可滚动?

[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;
        }
}

回答by Tim Hall

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 .scrollclass on it:

最近在 SPA 中遇到了同样的问题,其中<body>橡皮筋有损于体验,但我需要在子区域中滚动。非常感谢 dSquared 的建议,因为方法 1 最适合我。这是我对他的建议的小扩展,我在一个工作项目中实施了该建议,该项目一直向上查找树以查找任何具有.scroll类的元素(不仅仅是 div):

// 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:

这是 CSS 的样子:

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.

您只需要一个库(jQuery 或Zepto),您就可以实现原生滚动,并且身体上没有橡皮筋。此外,我添加了 translateZ 来解决我在滚动过程中元素消失时遇到的一些问题,它可用于GPU 加速元素

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.

但是(这是一个很大的但是),正如 dSquared 指出的那样,当滚动元素达到其极限并试图进一步滚动时,整个页面都会被橡皮筋束缚住。就个人而言,我认为这是一个失败,所以我继续努力,只是想尝试解决这个问题。沿着 OP 的代码行添加检查可能是答案,但我还没有尝试过。

UPDATE (10/7/12):

更新 (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.

经过大量工作,我得到了以下代码在 iOS6 中完美运行(没有在其他任何东西中测试过)。机身上没有橡皮筋,在滚动区域极限时不再出现问题,并且始终具有原生滚动性能。显然,最初的代码要多得多,但我认为这将使行为最接近 OP 的目标。

(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);

回答by dSquared

Unfortunately there isn't a 'magic bullet' fix for this as the rubber-band scrolling on Mobile Safari is an inbuilt 'feature' of the browser itself. By using any default scrolling mechanism provided by the browser, you will end up with the rubber-band scrolling to some degree.

不幸的是,没有针对此问题的“灵丹妙药”解决方案,因为 Mobile Safari 上的橡皮筋滚动是浏览器本身的内置“功能”。通过使用浏览器提供的任何默认滚动机制,您最终会在某种程度上使用橡皮筋滚动。

There are two ways I would suggest to tackle this:

我建议有两种方法来解决这个问题:

Method 1

方法一

Bind to the touchmoveevent on the </body>element and check the target of the touchmoveevent to see if you want it to fire or not like so:

绑定到元素touchmove上的事件</body>并检查touchmove事件的目标,看看您是否希望它像这样触发:

HTML

HTML

<div class="scroll">
    <p>...</p>
    <p>...</p>
</div>

JS

JS

$('body').on('touchmove', function(e) {
    // this is the node the touchmove event fired on
    // in this example it would be the </p> element
    target = e.target;

    // we need to find the parent container
    // we get it like so; assumes div as parent
    parent = $(e.target).closest('div');

    // check if the parent is a scroll window by class //
    if ($(parent).hasClass('scroll')){
        // ignore as we want the scroll to happen
    } else {
        e.preventDefault();
    }
});

JSFiddle Example Here

JSFiddle示例在这里

This method uses the default scrolling of the browser, however it has the drawback that you will still have the rubber-band scrolling when at the top or bottom of the scroll </div>.

此方法使用浏览器的默认滚动,但是它有一个缺点,即在 scroll 的顶部或底部时仍然会有橡皮筋滚动</div>

Method 2

方法二

Bind to the touchmoveevent of the </body>element as before, however in this case we prevent Alltouchmoveevents and rely on the excellent iScroll 4plugin to handle the scrolling, like so:

像以前一样绑定到元素的touchmove事件</body>,但是在这种情况下,我们阻止所有touchmove事件并依靠出色的iScroll 4插件来处理滚动,如下所示:

HTML

HTML

<div id="wrapper">
    <div id="scroller">
        <p>...</p>
        <p>...</p>
    </div>
</div>

JS

JS

$(document).ready(function(){
    // prevent all scroll //
    $('body').on('touchmove', function(e) {
        e.preventDefault();
    });

    // apply iscroll to scrolling element
    // requires use of id
    var newscroll = new iScroll('wrapper');
});??

JSFiddle Example Here

JSFiddle示例在这里

This is my preferred method as it blocks all rubber-band scrolling and provides a nice scrolling area, however it relies on the use of a plugin.

这是我的首选方法,因为它阻止了所有橡皮筋滚动并提供了一个不错的滚动区域,但是它依赖于插件的使用。

I hope this helps

我希望这有帮助

回答by Tobl

Here's a solution that uses jQuery and Hammer.js (jquery-implementation). That's two libraries, but if you're working on mobile, chances are you'll want to include Hammer anyway.

这是一个使用 jQuery 和 Hammer.js (jquery-implementation) 的解决方案。这是两个库,但是如果您在移动设备上工作,那么无论如何您很可能想要包含 Hammer。

For every drag-event that bubbles to the top (so non-scrolling drag-interactions can use stopPropagation) the handler checks if it bubbled through any elements with class=scrolling, if so, whether the user is scrolling within the allowed boundaries of that scrollContainer and only then does it permit native scrolling.

对于冒泡到顶部的每个拖动事件(因此非滚动拖动交互可以使用 stopPropagation),处理程序检查它是否冒泡通过 class=scrolling 的任何元素,如果是,则用户是否在允许的边界内滚动scrollContainer 并且只有这样它才允许本地滚动。

$("body").hammer().on('drag swipe', function(e){

    var scrollTarget = $(e.gesture.target).closest(".scrollable");
    if(scrollTarget.length)
    {
        var scrollTopMax = scrollTarget[0].scrollHeight - scrollTarget.outerHeight();
        if(scrollTopMax > 0){
            var scrollTop = scrollTarget.scrollTop();
            if(scrollTop > 0 && scrollTop < scrollTopMax){
                //console.log("scrolling in the middle");
            }
            else if(scrollTop <= 0 && e.gesture.deltaY < 0){
                //console.log("scrolling from top");
            }
            else if(scrollTop >= scrollTopMax && e.gesture.deltaY > 0){
                //console.log("scrolling from bottom");
            }
            else{
                //console.log("trying to scroll out of boundaries");
                e.gesture.preventDefault();
            }
        }
        else{
            //console.log("content to short to scroll");
            e.gesture.preventDefault();
        }
    }
    else{
        //console.log("no containing element with class=scrollable");
        e.gesture.preventDefault();
    }
});

To kill drags via pinch etc.; escape as necessary to allow zooming if your view is user-scalable

通过捏等杀死阻力;如果您的视图是用户可缩放的,则根据需要转义以允许缩放

$("body").hammer().on('doubletap rotate pinch', function(e){
    e.gesture.preventDefault();
});

Tested on ios7/safari, android4.3/webview and android4.3/firefoxMobile25 and the only solution that didn't break.

在 ios7/safari、android4.3/webview 和 android4.3/firefoxMobile25 上进行了测试,并且是唯一没有中断的解决方案。

回答by Mark

I wrote, in my opinion, the best solution for this problem. It will disable scrolling in general unless the element has y scrolling.

在我看来,我写了这个问题的最佳解决方案。除非元素具有 y 滚动,否则它通常会禁用滚动。

/********************************************************************************
 * Disable rubber band (c)2013 - Mark van Wijnen | www.CrystalMinds.nl
 ********************************************************************************/
$(function(){
    var scrollY = 0;

    $(document).on('touchstart', function( e ){
        scrollY = e.originalEvent.touches.item(0).clientY;
    });

    $(document).on('touchmove', function( e ){
        var scrollPos       = e.target.scrollTop;
        var scrollDelta     = scrollY - e.originalEvent.touches.item(0).clientY;
        var scrollBottom    = scrollPos + $(e.target).height();
        scrollY             = e.originalEvent.touches.item(0).clientY;

        if ( $(e.target).css( 'overflow-y' ) != 'scroll' || ( scrollDelta < 0 && scrollPos == 0 ) || ( scrollDelta > 0 && scrollBottom == e.target.scrollHeight ) ) 
            e.preventDefault();
    });
});

回答by Crashalot

Based on @Mark's answer, we came up with this alternative, which seems to work. Replace .page_listwith the class names of scrollable items.

根据@Mark 的回答,我们提出了这个替代方案,它似乎有效。替换.page_list为可滚动项的类名。

var INITIAL_Y = 0; // Tracks initial Y position, needed to kill Safari bounce effect

function kill_safari_bounce() {
    $( document ).on( 'touchstart', function( e ){
        INITIAL_Y = e.originalEvent.touches[0].clientY;
    });

    $( document ).on( 'touchmove', function( e ) {
        // Get scrollable ancestor if one exists
        var scrollable_ancestor = $( e.target ).closest( '.page_list' )[0];

        // Nothing scrollable? Block move.
        if ( !scrollable_ancestor ) {
            e.preventDefault();
            return;
        }

        // If here, prevent move if at scrollable boundaries.
        var scroll_delta = INITIAL_Y - e.originalEvent.touches[0].clientY;
        var scroll_pos = scrollable_ancestor.scrollTop;         
        var at_bottom = (scroll_pos + $(scrollable_ancestor).height()) == scrollable_ancestor.scrollHeight;

        if ( (scroll_delta < 0 && scroll_pos == 0) ||
             (scroll_delta > 0 && at_bottom) ){
            e.preventDefault();
        }    
    });
}

回答by ozgrozer

Finally I mixed some methods and these codes are the working version. But you must include the hammer.js

最后我混合了一些方法,这些代码是工作版本。但是你必须包含hammer.js

CSS

CSS

.scrollable{
    overflow:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;
    *{-webkit-transform:translate3d(0,0,0);}
}

JAVASCRIPT

爪哇脚本

$(document).on("touchmove",function(e){
    e.preventDefault();
});
$("body").on("touchstart",".scrollable",function(e){
    if(e.currentTarget.scrollTop===0){
        e.currentTarget.scrollTop = 1;
    }else if(e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight){
        e.currentTarget.scrollTop-=1;
    }
});
$("body").on("touchmove",".scrollable",function(e){
    e.stopPropagation();
});

$("body").hammer().on("pinch",function(e){
    e.gesture.preventDefault();
});

回答by MaZoli

Did anyone ever consider just using position fixed on the body? That is a nice, simple and native solution. No Javascript needed.

有没有人考虑过只使用固定在身体上的位置?这是一个不错的、简单的、原生的解决方案。不需要Javascript。

body{
    position: fixed;
}

回答by Raul Brito

Just add a -webkit-overflow-scrolling: auto;to the div you want to prevent from bouncing

只需在-webkit-overflow-scrolling: auto;要防止弹跳的 div添加一个