如何在 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 中遇到了同样的问题,其中<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();
);
下面是 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 上的橡皮筋滚动是浏览器本身的内置“功能”。通过使用浏览器提供的任何默认滚动机制,您最终会得到某种程度的橡皮筋滚动。
我建议有两种方法来解决这个问题:
方法一
绑定到</body>
元素上的touchmove
事件并检查touchmove
事件的目标,看看您是否希望它像这样触发:
<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
此方法使用浏览器的默认滚动,但是它的缺点是在滚动的顶部或底部时仍然会有橡皮筋滚动</div>
。
方法二
像以前一样绑定到</body>
元素的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 上进行了测试,并且是唯一没有崩溃的解决方案。
【讨论】:
需要在 CSStouch-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();
);
);
【讨论】:
嗨@mark,如果您对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 中禁用来自 Web 服务调用的自动排序 JSON 字典响应