document.ontouchmove和iOS 5上的滚动问题

47
iOS 5给JavaScript/Web应用带来了许多好东西,其中之一是改进的滚动功能。如果你添加

-webkit-overflow-scroll:touch;

将样式设置为textarea元素的样式,就可以使用一个手指轻松地滚动。

但是有一个问题。为了防止整个屏幕滚动,建议Web应用程序添加以下代码:

document.ontouchmove = function(e) {e.preventDefault()};

然而,这将禁用新的滚动条。

有没有一种好的方法可以允许在文本区域内使用新的滚动条,但不允许整个表单滚动?


你可以尝试使用yourScrollElement.ontouchmove=function(e) {e.preventDefault()}; 不确定是否有效。 - Gerben
1
不行,那样做不了。谢谢! - ghenne
可能存在一些混淆,因为Gerben的建议与Brian Nickel在下面提出的备选建议完全相同。 - Elliot Nelson
@ElliotNelson 在我使用 preventDefault 的地方,他使用了 stopPropagation。不确定为什么那样会起作用。 - Gerben
一处更正:CSS样式属性的名称应为"-webkit-overflow-scrolling",而不是"-webkit-overflow-scroll"。@ghenne - Hongfei
5个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
56

更新 根据Alvaro的评论,这种解决方案可能无法在iOS 11.3及之后的版本中正常工作。

你可以通过选择是否调用preventDefault来允许滚动。例如:

document.ontouchmove = function(e) {
    var target = e.currentTarget;
    while(target) {
        if(checkIfElementShouldScroll(target))
            return;
        target = target.parentNode;
    }

    e.preventDefault();
};

或者,也可以通过阻止事件传递到文档级别来实现。

elementYouWantToScroll.ontouchmove = function(e) {
    e.stopPropagation();
};

编辑 对于后来的读者,备选答案确实可行且更加简单。


很棒的简单解决方案。另一种jQuery插件解决方案http://www.hakoniemi.net/labs/nonbounce/ - falko
我认为你应该使用 e.target 而不是 e.currentTarget,因为 e.currentTarget 总是指向监听器所附加的元素, 在这种情况下是 document - Derek Henderson
@Alvaro,你找到解决办法了吗? - Kellen
2
@Kellen 是的,请使用touchstart,参见我的这个问题: https://dev59.com/y1UL5IYBdhLWcg3wtp5m - Alvaro

23

Brian Nickel的回答唯一的问题是(如user1012566所提到的),stopPropagation无法防止在滚动区域边界处冒泡。您可以通过以下方式来防止这种情况:

elem.addEventListener('touchstart', function(event){
    this.allowUp = (this.scrollTop > 0);
    this.allowDown = (this.scrollTop < this.scrollHeight - this.clientHeight);
    this.prevTop = null; 
    this.prevBot = null;
    this.lastY = event.pageY;
});

elem.addEventListener('touchmove', function(event){
    var up = (event.pageY > this.lastY), 
        down = !up;

    this.lastY = event.pageY;

    if ((up && this.allowUp) || (down && this.allowDown)) 
        event.stopPropagation();
    else 
        event.preventDefault();
});

我无法在IOS6上使其工作,实际上它还阻止了PC浏览器的滚动。在IOS上,只有在您已经滚动过边界之后,它才会防止弹性滚动。 - Phedg1
3
这个解决方案似乎并不完全有效。当滚动条到达顶部或底部时,元素会停留在当前位置,导致我无法向任何方向滚动。我正在使用iOS 7.1。 - Derek Henderson
是的,正是他所说的。 - Phedg1
1
请修复代码,将所有 event.pageY 替换为 event.targetTouches[0].pageY - ehynds

16

如果有人想在 PhoneGap 上实现这个功能,可以在 cordova.plist 中禁用弹性滚动,在 UIWebViewBounce 的值设为 NO。我希望这能帮助那些像我一样花了很长时间来解决这个问题的人。


1
谢谢你,安德鲁!我只花了半个时代才找到这个! - mharr
谢谢!这节省了我大量的时间去追踪错误! - Gabriel Isenberg
此外还有其他选项/文档:http://tripleshotsoftware.blogspot.com/2012/09/stop-uiwebview-bounce-for-cordova-based.html - XML

7

ScrollFix似乎是完美的解决方案。我测试了它,它的效果非常好!

https://github.com/joelambert/ScrollFix

/**
 * ScrollFix v0.1
 * http://www.joelambert.co.uk
 *
 * Copyright 2011, Joe Lambert.
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 */

var ScrollFix = function(elem) {
    // Variables to track inputs
    var startY, startTopScroll;

    elem = elem || document.querySelector(elem);

    // If there is no element, then do nothing  
    if(!elem)
        return;

    // Handle the start of interactions
    elem.addEventListener('touchstart', function(event){
        startY = event.touches[0].pageY;
        startTopScroll = elem.scrollTop;

        if(startTopScroll <= 0)
            elem.scrollTop = 1;

        if(startTopScroll + elem.offsetHeight >= elem.scrollHeight)
            elem.scrollTop = elem.scrollHeight - elem.offsetHeight - 1;
    }, false);
};

2
发现stopPropagation和本地div滚动存在已知问题是非常令人沮丧的。它似乎无法阻止onTouchMove事件冒泡,因此当滚动超出div的边界(在顶部向上或在底部向下)时,整个页面会反弹。 更多讨论请参见这里这里

请注意,您可以通过下面列出的解决方案来避免这种情况。 - 1nfiniti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,