Как предотвратить "навигацию по истории превышения прокрутки", не предотвращая сенсорный запуск?

Я использую навигацию на основе смахивания, и у меня возникают проблемы с Chrome.

Недавно реализованная функция "Навигация по истории с избыточной прокруткой" запускается при перетаскивании страницы вправо, вызывая откат назад (к "истории -1"). Чтобы предотвратить это, я должен был бы позвонить .preventDefault() на touchstart, но это также отключает все от нажатия ссылок на прокрутку. Как предотвратить события пользовательского интерфейса браузера, не мешая стандартной странице?

Полное отключение этой функции путем установки соответствующего флага в Chrome устраняет проблему, но не является практичным для общедоступного приложения. хром:// флаги /#overscroll-история-навигация

4 ответа

В конце концов я нашел решение:

Хром стреляет как минимум touchstart а также touchmove до перескрутки. Отслеживая направление этих событий, избыточная прокрутка может быть отфильтрована от обычной прокрутки.

Я написал код для этой проблемы Hammer.js.

Я использовал

html, body {
    width: 100%;
    height: 100%;
    margin: 0;
    overscroll-behavior: contain;    
}

https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior

Это 'touchmove', вам нужно будет stopPropagation() И preventDefault() внутри обратного вызова, вы можете вызвать все, что вам нужно, для проверки прокрутки и вашей навигации.

  • слушать onscroll событие в контейнере и сохранить значение "scrollLeft" this.offsetX = event.currentTarget.scrollLeft;
  • слушать onwheel событие в том же контейнере и использовать этот обработчик

var offset = 0;

document.getElementById("1")
  .addEventListener("scroll", function(event) {
     offset = event.currentTarget.scrollLeft;
  });
document.getElementById("1")
  .addEventListener("wheel", function(event) {
    // if we reach the left side of the scrollable content, and we scroll further -> block the event

    if (offset === 0 && event.deltaX <= 0) {
      event.preventDefault();
    }
  });
  
  
.container {
  width: 100%;
  background-color: blue;
  overflow: auto;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}

.element {
  display: inline-block;
  min-width: 100px;
  height: 50px;
  margin: 10px;
  background-color: red;
}
<div id="1" class="container">
  <div class="element">
  1
  </div>
  <div class="element">
  2
  </div>
  <div class="element">
  3
  </div>
  <div class="element">
  4
  </div>
  <div class="element">
  5
  </div>
  <div class="element">
  6
  </div>
  <div class="element">
  7
  </div>
  <div class="element">
  8
  </div>
</div>

Чтобы оставить прокрутку по вертикали рабочим и перемещать элементы по горизонтали, вам нужно проверить, больше ли перемещается вдоль оси x, чем по оси y, сравнивая различия координат с помощью Math.abs(), затем решите вызвать.preventDefault() в событии touchmove или нет.

Звучит немного странно, но я думаю, что это единственный способ управлять этой функцией бегемота "Навигация по истории Overscroll". BLE.

$(function () {

    function extract(e) {
        if (e.changedTouches) {
            e = e.changedTouches;
            if (e['0'])
                e = e['0'];
        }
        return e;
    }

    var div = $('div').html('Drag me left and right and see that Overscroll is disabled & drag me up and down to see that scroll still works<br /><br /><br />'.repeat(300));
    var di  = div.get(0); // get native dom element

    var startx, lastx, lasty, l = false, active = false;

    di.addEventListener("touchstart", function (e) {
        active = true;
        e = extract(e);

        // init
        lastx = e.pageX;
        lasty = e.pageY;

        l = parseInt(div.css('left'), 10);
        startx = e.pageX;
    }, false);

    di.addEventListener("touchmove", function (ee) {
        if (active) {
            var e = extract(ee);

            // check if preventDefault to cancel Overscroll history navigation only if it's needed
            if (Math.abs(lastx - e.pageX) > Math.abs(lasty - e.pageY)) {
                ee.preventDefault();
            }

            // update x y to next above calculation
            lastx = e.pageX;
            lasty = e.pageY;

            // repositioning
            div.css({left: (l + (e.pageX - startx))+'px'})
        }
    }, false);

    di.addEventListener("touchend", function (e) {
        active = false;
    }, false);
});

Полный пример для тестирования на сенсорных устройствах: DEMO

Другие вопросы по тегам