Lg102
Lg102

Reputation: 4898

How to prevent 'Overscroll history navigation' without preventing touchstart?

I'm implementing a swipe-base navigation, and I'm running into trouble with Chrome.

A newly implemented feature, 'Overscroll history navigation', is triggered when the page is dragged to the right, causing a jump back (to 'history -1'). To prevent this, I'd have to call .preventDefault() on touchstart, but this also disables everything from clicking links to scrolling. How do I prevent browser UI events without interfering with the standard page?

Disabling the feature altogether by setting the appropriate flag in chrome fixes the issue, but isn't practical for a public-facing application. chrome://flags/#overscroll-history-navigation

Upvotes: 3

Views: 6157

Answers (5)

pedrofuenteszeg0
pedrofuenteszeg0

Reputation: 1

It's 'touchmove' you will have to stopPropagation() AND preventDefault() inside the callback you can call whatever you need for checking the swipe and your navigation to happen.

Upvotes: 0

Martijn B
Martijn B

Reputation: 4075

I used

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

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

Upvotes: 0

Florin David
Florin David

Reputation: 1

  • listen to onscroll event on the container and store the 'scrollLeft' value this.offsetX = event.currentTarget.scrollLeft;
  • listen to onwheel event on the same container and use this handler

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>

Upvotes: 0

stopsopa
stopsopa

Reputation: 464

To leave scrolling vertical working and moving elemets horizontally you need to check if moving is more along x then y axis by comparing coordinates differences using Math.abs(), then decide to call .preventDefault() in touchmove event or not.

Sounds little strange but i think it is the only way to control this behemoth feature "Overscroll history navigation". 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);
});

Complete example to test on touch devices: DEMO

Upvotes: 0

Lg102
Lg102

Reputation: 4898

I eventually figured out a solution:

Chrome fires at least touchstart and touchmove before overscrolling. By tracking the direction of those events, an overscroll can be filtered from regular scrolling.

I've written up the code for this Hammer.js issue.

Upvotes: 2

Related Questions