问题
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
回答1:
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.
回答2:
I used
html, body {
width: 100%;
height: 100%;
margin: 0;
overscroll-behavior: contain;
}
https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior
回答3:
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
回答4:
- listen to
onscroll
event on the container and store the 'scrollLeft' valuethis.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>
来源:https://stackoverflow.com/questions/23560528/how-to-prevent-overscroll-history-navigation-without-preventing-touchstart