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

Posted

技术标签:

【中文标题】如何在 iOS 网络应用程序中禁用橡皮筋?【英文标题】:How to disable rubber band in iOS web apps? 【发布时间】:2012-05-08 15:03:32 【问题描述】:

这个:

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

有效,但会禁用整个页面的滚动,这远非理想。

这个:

$('*').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;
);

适用于具有滚动区域的页面。但是,当没有任何内容可滚动时,它会再次显示橡皮筋。

所以我的问题:

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

[更新]

最佳解决方案

禁用所有不可滚动元素的滚动,例如标签栏或导航栏。

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

将滚动处理程序附加到可滚动元素,例如主要内容。

anElement.addEventListener('touchstart', function( event )
        if( this.scrollTop === 0 ) 
            this.scrollTop += 1;
         else if( this.scrollTop + this.offsetHeight >= this.scrollHeight ) 
            this.scrollTop -= 1;
        

【问题讨论】:

我也有同样的问题! 我也在寻找答案。在桌面 Safari 上,设置 body overflow:hidden; height: 100%; 会有所帮助,但这在 ios 上不起作用。 只是出于好奇,您为什么要禁用它? 原因很简单,它感觉更像是一个原生应用程序。 【参考方案1】:

最近在一个 SPA 中遇到了同样的问题,其中&lt;body&gt; 橡皮筋影响了体验,但我需要在子区域中滚动。非常感谢 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();
    
);

下面是 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; 

您只需要一个库(jQuery 或Zepto),您就可以获得具有动量的原生滚动,并且主体上没有橡皮筋。另外,我添加了 translateZ 来解决我在滚动过程中元素消失的一些问题,它可以用于GPU accelerate your elements。

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

更新(2012 年 10 月 7 日):

经过大量工作,我得到了以下代码在 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);

【讨论】:

我可能在这个问题上花了大约两个月的时间,但走了一条完全错误的路线。 YTMND。 等等!我说得太早了。如果您尝试在 scroll-x 元素上向上拖动手指,它仍然会很遗憾地被橡皮筋缠住。 :( 我使用的是 ipad(不是第 6 版),我将 scroll-y 类放在对话框中。它可以防止橡皮筋向上,但不能防止橡皮筋向下。知道如何解决这个问题吗? 虽然这似乎可以改善问题,但我仍然可以通过拉动子区域来强制身体使用橡皮筋,而无需太多力气。 这绝对是一流的。我使用的是github.com/pinadesign/overscroll,但其中一个问题是您无法从文本区域启动触摸滚动,因此具有大文本区域的表单变得无法使用。谢谢大家。【参考方案2】:

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

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

方法一

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

html

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

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

此方法使用浏览器的默认滚动,但是它的缺点是在滚动的顶部或底部时仍然会有橡皮筋滚动&lt;/div&gt;

方法二

像以前一样绑定到&lt;/body&gt; 元素的touchmove 事件,但是在这种情况下,我们阻止了 All touchmove 事件并依靠出色的iScroll 4 插件来处理滚动,就像这样:

HTML

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

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

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

希望对你有帮助

【讨论】:

谢谢,解决方案 1 解决了我们与此问题相关的问题! target = e.target 没有被使用。【参考方案3】:

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

body
    position: fixed;

【讨论】:

我认为这并不适用于所有情况,但由于我使用的是 React 并且无法轻松访问 DOM,这正是我所需要的。 它适用于大多数情况。如果您拖动“通过”位置固定元素,则会出现短暂的输入延迟。但你应该没事。【参考方案4】:

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

对于每个冒泡到顶部的拖动事件(因此非滚动拖动交互可以使用 stopPropagation),处理程序检查它是否冒泡通过任何具有 class=scrolling 的元素,如果是,则用户是否在允许的范围内滚动该滚动容器的边界,然后它才允许本机滚动。

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

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

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

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

【讨论】:

需要在 CSS touch-action 上进行哪些设置才能完成这项工作? 答案比触摸动作早了很多,所以自动;但希望现在有更好的解决方案可用。 其实问题还是存在的,解决方法还是有问题的。查看my SO post对问题的一些深入分析和一些总结的解决方法。在我的案例中,最终的诀窍是脚本iNoBounce。 Tbh,这种脚本就是我所说的“更好的解决方案”。抽象听起来与我的解决方案类似,但将其作为脚本让我们可以轻松实现和集中错误跟踪。我看到的唯一问题是专注于 iOS,在 Android 上一文不值?【参考方案5】:

在我看来,我写了这个问题的最佳解决方案。除非元素有 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();
    );
);

【讨论】:

嗨@mar​​k,如果您对iPhone Web 应用程序的HTML5 咨询感兴趣,您介意通过电子邮件发送信息到panabee dot com 吗?谢谢!一个建议:有些人可能会指定“auto”作为值而不是“scroll”。 还有必要在touchmove中更新scrollY吗?只是与原来的水龙头比较应该指示方向,对吧? 我写代码已经有一段时间了,但我猜我正在更新 scrollDelta 属性的 scrollY。当您已经在顶部时在可滚动区域中向上滚动时,也会发生弹跳,这可以防止这种情况发生。 HTML5 咨询是什么意思? @Mark 既然你如此仔细地包含了版权声明,你能澄清一下你发布这个 sn-p 的许可证吗?【参考方案6】:

根据@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();
            
    );

【讨论】:

@Crashalot 你能做一个工作小提琴吗,我不能让它工作,我一定错过了一些东西。谢谢【参考方案7】:

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

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

【讨论】:

【参考方案8】:

只需将-webkit-overflow-scrolling: auto; 添加到要防止弹跳的 div 中

【讨论】:

也终止任何形式的平滑滚动。

以上是关于如何在 iOS 网络应用程序中禁用橡皮筋?的主要内容,如果未能解决你的问题,请参考以下文章

如何在整个应用程序中禁用 iOS 11 拖动?

如何在智能手机中运行网络应用程序时禁用任何来电?

如何在 iOS 中禁用来自 Web 服务调用的自动排序 JSON 字典响应

如何在IOS中禁用自动省略号

IOS端小程序橡皮筋回弹效果解决方案及坑总结(uni-swipe-action在IOS端的坑)

IOS端小程序橡皮筋回弹效果解决方案及坑总结(uni-swipe-actiond在IOS端的坑)