Условно заблокировать событие прокрутки / сенсорного перемещения в мобильном сафари - PullRequest
7 голосов
/ 23 декабря 2011

В iOS 5 теперь разрешено собственное переполнение: поддержка прокрутки.

Я хотел бы отключить событие touchmove для всего, кроме элементов, которые имеют класс «прокручиваемый» или их потомков.

Но я не могу заставить его работать;Вот с чем я работал ниже:

<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>

Ответы [ 6 ]

15 голосов
/ 10 мая 2012

Я знаю, что прошло много времени с тех пор, как вы задали вопрос, но у меня возникла та же проблема, и я использовал ваш код в качестве основы для решения проблемы. Так что спасибо за вдохновение.

(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>

Или менее многословно, но в конечном итоге тот же результат (кредит Дж. Гриффитс):

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

Вы также должны включить следующий тег META.

<meta name="viewport" content="width=device-width, initial-scale=1.0,
  maximum-scale=1.0, user-scalable=no;" />
3 голосов
/ 27 февраля 2013

Версия 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();
});
2 голосов
/ 20 февраля 2013

Если вы напишите это в jquery document.ready событие, оно будет работать.

$('body').on('touchmove', function (e) {
  if ($(e.target).closest("your_scrollable_div_selector").length == 0)
    e.preventDefault();
});
1 голос
/ 06 сентября 2012

Я попробовал ответ Скотта, но он не работал на моем iphone iOS 5.1.1

Кроме того, это особенно важно, если вы создаете приложение webClip, черт возьми, я надеюсь, что iOS 6 разрешит тег viewport, который отключает автоматический отскок

Моя версия ниже работает (или не работает) так же, как и ответ Скотта выше, поскольку по сути это то же самое.

jQuery 1.7.2

        $(document).bind("touchmove",function(e){
            e.preventDefault();
        });
        $('.scrollable').bind("touchmove",function(e){
            e.stopPropagation();
        });
0 голосов
/ 07 марта 2015

Мы можем использовать событие touchstart вместо события touchmove .В разделе «События одного пальца» говорится, что во время панорамирования никакие события не отправляются, поэтому сенсорное перемещение может быть слишком поздним.

Я добавил слушателя в документ, а не в тело.

document.ontouchstart = function(e){ 
  e.preventDefault(); 
}
0 голосов
/ 10 января 2012

Вот (в основном) рабочее решение для отключения вертикальной прокрутки для всех элементов, кроме переполненных:

(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()
...