Can't prevent `touchmove` from scrolling window on iOS
Asked Answered
L

3

37

We are trying to scroll an element on our iOS web app while preventing the window itself from scrolling. We are capturing the touchmove event on the window, scrolling the element programmatically and (trying to) prevent the window itself from scroll by calling preventDefault on the event.

Unfortunately this doesn't work in Mobile Safari. The window continues to scroll underneath our element. The issue sounds exactly like the Webkit bug described in https://bugs.webkit.org/show_bug.cgi?id=163207, but that issue was supposedly fixed in iOS 10.3 whereas I am running 11.3.

Catching touchforcestart and calling preventDefault does seem to prevent scrolling of the window, but we are calling it in touchstart, which seems to be "too late" since the window still scrolls. Scrolling is only prevented next time touchstart is called.

Any ideas about what is going on? We are baffled since this is clearly a bug but it seems to have been fixed some time ago.

Laski answered 26/3, 2018 at 20:46 Comment(0)
S
89

I recently ran into this same problem. You'll need to pass { passive: false } when registering the touchmove event listener. e.g.

document.addEventListener('touchmove', function(e) {
    e.preventDefault();
}, { passive: false });

This is because document touch event listeners are now passive by default in Safari 11.1, which is bundled with iOS 11.3. This change is documented in the Safari 11.1 release notes:

Web APIs

  • [...]
  • Updated root document touch event listeners to use passive mode improving scrolling performance and reducing crashes.
Santoro answered 30/3, 2018 at 22:25 Comment(8)
Great stuff, thanks! We are adding our touchmove listener inside the touchstart handler, and for some reason it seems I need to cancel that even as well (with preventDefault) to get the touchmove to cancel, even with passive turned off. With both { passive: false } and the touchstart and touchmove events both canceled after we handle them, it appears to work great.Laski
Is it possible to enable scroll in specific div only inside the document after applying this code? @SantoroRakes
How do I remove this event listener on click from a page? @SantoroRakes
Nice, but the problem now is you're not scrolling the page either.Intrastate
@MatthewGertner -- do you mean you add: document.addEventListener('touchstart', function(e) { e.preventDefault(); }, { passive: false }); document.addEventListener('touchmove', function(e) { e.preventDefault(); }, { passive: false });Parsec
Thank you so much. I kept seeing answers that didn't have the passive part, and so didn't work.Sothena
Awesome! the missing passive: false drove me nuts for hours! Thank you!Brandabrandais
how do i pass in the { passive: false } option to a function delcaration?Prodrome
P
9

You need to bind preventDefault to two events: touchmove and touchforcechange to make it work in ios 11, e.g.

document.addEventListener('touchmove', this.preventDefault, {passive: false});
document.addEventListener('touchforcechange', this.preventDefault, {passive: false});

And you should bind them before touchstart

If you bind them inside your touchstart or dragStart handler, they can only prevent scroll in the next dragging.

Parsley answered 18/8, 2019 at 6:42 Comment(1)
touchforcechange? it seems to work without that?Prodrome
C
2

What worked for me was to pass the {passive:false} option to addEventListener (explanation), and you also need to make sure you execute e.preventDefault() on touchmove and touchstart:

window.addEventListener("touchstart", e=>e.preventDefault(), {passive:false});
window.addEventListener("touchmove", e=>e.preventDefault(), {passive:false});
Coextensive answered 6/4, 2022 at 7:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.