如何禁用iOS网络应用中的橡皮筋?

这个:

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

可以工作,但会在整个页面中禁用滚动,这远非理想。

这个:

 $('*').on('touchstart', function(e){ var element = $(this).get(0); if ( element.scrollTop = 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; } } 

最近在SPA中出现了同样的问题,其中橡皮筋正在减损体验,但我需要在子区域滚动。 非常感谢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加速你的元素 。

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

更新(10/7/12):

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

不幸的是,没有一个“神奇的子弹”修复,因为Mobile Safari上的橡皮筋滚动是浏览器本身的内置“function”。 通过使用浏览器提供的任何默认滚动机制,您将最终在某种程度上进行橡皮筋滚动。

我建议有两种解决方法:

方法1

绑定到元素上的touchmove事件并检查touchmove事件的目标,以查看是否要触发它或者不是这样:

HTML

 

...

...

JS

 $('body').on('touchmove', function(e) { // this is the node the touchmove event fired on // in this example it would be the  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示例在这里

此方法使用浏览器的默认滚动,但它的缺点是在滚动的顶部或底部仍然会有橡皮筋滚动。

方法2

像以前一样绑定到元素的touchmove事件,但在这种情况下,我们会阻止所有 touchmove事件并依赖优秀的iScroll 4插件来处理滚动,如下所示:

HTML

 

...

...

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示例在这里

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

我希望这有帮助

这是一个使用jQuery和Hammer.js(jquery-implementation)的解决方案。 这是两个图书馆,但是如果你在移动设备上工作,那么你很可能还想要包括Hammer。

对于每个冒泡到顶部的拖动事件(因此非滚动拖动交互可以使用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(); } }); 

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

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

测试了ios7 / safari,android4.3 / webview和android4.3 / firefoxMobile25以及唯一没有破解的解决方案。

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

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

最后我混合了一些方法,这些代码是工作版本。 但是你必须包括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(); }); 

只需添加-webkit-overflow-scrolling: auto; 你希望防止弹跳的div