此:
$('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;
}
}
答案 0 :(得分:30)
最近出现了同样的问题,其中<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。
BUT(这是一个很大的但是),正如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);
答案 1 :(得分:8)
不幸的是,由于Mobile Safari上的橡皮筋滚动是浏览器本身的内置“功能”,因此没有“神奇的子弹”修复。通过使用浏览器提供的任何默认滚动机制,您将最终在某种程度上进行橡皮筋滚动。
我建议有两种解决方法:
方法1
绑定到touchmove
元素上的</body>
事件并检查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 强>
此方法使用浏览器的默认滚动,但它的缺点是,在滚动页面</div>
的顶部或底部仍然会滚动橡皮筋。
方法2
与以前一样绑定到touchmove
元素的</body>
事件,但在这种情况下,我们会阻止所有 touchmove
事件,并依赖于优秀的{{ 3}}插件来处理滚动,如下所示:
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');
});
<强> iScroll 4 强>
这是我首选的方法,因为它会阻止所有橡皮筋滚动并提供一个漂亮的滚动区域,但它依赖于使用插件。
我希望这会有所帮助
答案 2 :(得分:2)
这是一个使用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以及唯一没有破解的解决方案。
答案 3 :(得分:1)
在我看来,我写了这个问题的最佳解决方案。它将禁用一般滚动,除非元素有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();
});
});
答案 4 :(得分:1)
根据@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();
}
});
}
答案 5 :(得分:0)
最后我混合了一些方法,这些代码是工作版本。 但您必须包含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();
});
答案 6 :(得分:0)
有人曾经考虑只使用固定在身体上的姿势吗? 那是一个不错的,简单的本地解决方案。不需要Javascript。
body{
position: fixed;
}
答案 7 :(得分:-1)
只需在想要防止弹跳的div中添加-webkit-overflow-scrolling: auto;