Javascript 有条件地阻止移动 Safari 中的滚动/触摸移动事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8610486/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 06:42:55  来源:igfitidea点击:

Conditionally block scrolling/touchmove event in mobile safari

javascriptjquerywebkitcordovamobile-safari

提问by ???u

iOS 5 now allows native overflow: scroll support.

iOS 5 现在允许原生溢出:滚动支持。

What I'd like to do is disable the touchmoveevent for everything except elements that have the 'scrollable' class or their children.

我想做的是禁用touchmove除具有“可滚动”类或其子项的元素之外的所有内容的事件。

But I can't seem to get it to work; this is what I've been working with below:

但我似乎无法让它发挥作用;这就是我一直在使用的内容:

<html>
<head>
<style>
.scrollable {
 height: 5em;
 overflow-y: scroll;
 -webkit-overflow-scrolling: touch;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script>

// doesn't seem to work
var handleMove = function (e) {
  if (!$(e.target).parents().andSelf().hasClass('scrollable')) {
    e.preventDefault();
  }
};

document.addEventListener('touchmove', handleMove, true);

</script>
</head>
<body>
<div>
don't scroll if you drag here
</div>
<div class='scrollable'>
should be scrollable if you drag here
<ul>
<li>and here</li>
<li>and here</li>
<li>and here</li>
<li>and here</li>
<li>and here</li>
<li>and here</li>
<li>and here</li>
<li>and here</li>
</ul>
</div>
don't scroll if you drag here
</body>
</html>

回答by Scott

I know it's been a while since you asked the question, but I had the same issue and I used your code as the basis for solving the problem. So thanks for the inspiration.

我知道你问这个问题已经有一段时间了,但我遇到了同样的问题,我用你的代码作为解决问题的基础。所以谢谢你的灵感。

(Javascript + jQuery)

(Javascript + jQuery)

<script>
var handleMove = function (e) {
    var scrollable = false;
    var items = $(e.target).parents();
    $(items).each(function(i,o) {
        if($(o).hasClass("scrollable")) {
            scrollable = true;
        }
    });
    if(!scrollable)
        e.preventDefault();
};
document.addEventListener('touchmove', handleMove, true);
</script>

Or less verbose, but ultimately the same result (credit J Griffiths):

或者不那么冗长,但最终结果相同(信用 J Griffiths):

<script>
var handleMove = function (e) {
    if($(e.target).closest('.scrollable').length == 0) { e.preventDefault(); }
}
document.addEventListener('touchmove', handleMove, true);
</script>

You should also include the following META tag.

您还应该包含以下 META 标记。

<meta name="viewport" content="width=device-width, initial-scale=1.0,
  maximum-scale=1.0, user-scalable=no;" />

回答by Fred Bergman

JavaScript version based on Nevirs answer:

基于 Nevirs 答案的 JavaScript 版本:

var initialY = null;
var nodeStack = [];
var $window = $(window);

$window.bind('touchstart', function(e) {
    initialY = e.originalEvent.pageY;
    nodeStack = $(e.target).parents().andSelf().filter(':not(body, html)').get().reverse();
    nodeStack = nodeStack.map(function(node) {
        return $(node);
    });
});

$window.bind('touchend touchcancel', function(e) {
    initialY = null;
    nodeStack = [];
});

$window.bind('touchmove', function(e) {

    if (!initialY) {
        e.preventDefault();
    }

    var direction = e.originalEvent.pageY - initialY;

    for (var i = 0; i < nodeStack.length; i +=1) {
        var $node = nodeStack[i];
        var nodeHeight = $node.height();
        var scrollHeight = $node[0].scrollHeight - 2;
        var nodeScrollTop = $node.scrollTop();

        if (scrollHeight > nodeHeight) {
            // the user is dragging the content up, and the element is already scrolled down a bit.
            var allowedUp = direction > 0 && nodeScrollTop > 0;

            // the user is dragging the content down, and the element is up a bit.
            var allowedDown = direction < 0 && nodeScrollTop < scrollHeight - nodeHeight;

            if (allowedUp || allowedDown) {
                return;
            }
        }
    }

    // disable drag
    e.preventDefault();
});

回答by sems

If you write this in jquery document.ready event it will work.

如果您在 jquery document.ready 事件中编写它,它将起作用。

$('body').on('touchmove', function (e) {
  if ($(e.target).closest("your_scrollable_div_selector").length == 0)
    e.preventDefault();
});

回答by tim

I tried Scott's answer but it didn't work on my iphone iOS 5.1.1

我尝试了 Scott 的回答,但它在我的 iphone iOS 5.1.1 上不起作用

Also, this is particularly important if you're building a webClip app, gosh I do hope iOS 6 will allow a viewport tag that disables the auto-bounce

此外,如果您正在构建 webClip 应用程序,这尤其重要,天哪,我确实希望 iOS 6 将允许禁用自动反弹的视口标签

My version below works (or doesn't) as well as Scott's answer above, as it essentially does the same thing.

我下面的版本和上面 Scott 的回答一样有效(或无效),因为它基本上做同样的事情。

jQuery 1.7.2

jQuery 1.7.2

        $(document).bind("touchmove",function(e){
            e.preventDefault();
        });
        $('.scrollable').bind("touchmove",function(e){
            e.stopPropagation();
        });

回答by Pavan

We can use the touchstartevent instead of touchmoveevent. Under One Finger Events it says that no events are sent during a pan, so touchmove may be too late.

我们可以使用touchstart事件代替touchmove事件。在 One Finger Events 下,它表示在平移期间没有发送任何事件,因此 touchmove 可能为时已晚。

I added the listener to document, not body.

我将侦听器添加到文档,而不是正文。

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

回答by Nevir

Here's a (mostly) working solution for disabling vertical scrolling for all but overflowed elements:

这是一个(主要)工作解决方案,用于禁用除溢出元素之外的所有元素的垂直滚动:

(CoffeeScript):

(咖啡脚本):

# Vertical scrolling behavior overrides.
#
# This disables vertical scrolling on the page for touch devices, unless the user is scrolling
# within an overflowed node.  This requires some finessing of the touch events.
#
# **NOTE:** This code ends up disabling bounce behavior if the user tries to scroll on a node that
# is already at its upper or lower limit.
window$   = $(window)
initialY  = null
nodeStack = []

# When a user begins a (potential) drag, we jot down positional and node information.
#
# The assumption is that page content isn't going to move for the duration of the drag, and that
# it would also be awkward if the drag were to change/stop part way through due to DOM
# modifications.
window$.bind 'touchstart', (evt) ->
  initialY  = evt.originalEvent.pageY
  nodeStack = $(evt.target).parents().andSelf().filter(':not(body, html)').get().reverse()
  nodeStack = nodeStack.map (node) -> $(node)

window$.bind 'touchend touchcancel', (evt) ->
  initialY  = null
  nodeStack = []

# We override the `touchmove` event so that we only allow scrolls in allowable directions,
# depending on where the user first began the drag.
window$.bind 'touchmove', (evt) ->
  return evt.preventDefault() if initialY == null
  # A positive direction indicates that the user is dragging their finger down, thus wanting the
  # content to scroll up.
  direction = evt.originalEvent.pageY - initialY

  for node$ in nodeStack
    nodeHeight    = node$.height()
    # For some reason, the node's scrollHeight is off by 2 pixels in all cases.  This may require
    # tweaking depending on your DOM.  Concerning.
    scrollHeight  = node$[0].scrollHeight - 2
    nodeScrollTop = node$.scrollTop()

    # If we have a scrollable element, we want to only allow drags under certain circumstances:
    if scrollHeight > nodeHeight
      # * The user is dragging the content up, and the element is already scrolled down a bit.
      return if direction > 0 and nodeScrollTop > 0
      # * And the reverse: the user is dragging the content down, and the element is up a bit.
      return if direction < 0 and nodeScrollTop < scrollHeight - nodeHeight

  # Otherwise, the default behavior is to disable dragging.
  evt.preventDefault()