118

I have a page with a section to sketch a drawing in. But the touchmove events, at least the vertical ones, are also scrolling the page (which degrades the sketching experience) when using it on a mobile browser. Is there a way to either a) disable & re-enable the scrolling of the page (so I can turn it off when each line is started, but turn it back on after each is done), or b) disable the default handling of touchmove events (and presumably the scrolling) that go to the canvas the sketch is drawn in (I can't just disable them completely, as the sketching uses them)?

I've used jquery-mobile vmouse handlers for the sketch, if that makes a difference.

Update: On an iPhone, if I select the canvas to be sketched in, or just hold my finger for a bit before drawing, the page doesn't scroll, and not because of anything I coded in the page.

Scott Hunter
  • 48,888
  • 12
  • 60
  • 101
  • 2
    Pretty sure it's not gonna help you a lot in the form it is present there, but have a look at [this question](http://stackoverflow.com/questions/4770025/how-to-disable-scrolling-temporarily). It deals with regular scrolling, but maybe you could use it. – Kiruse May 02 '13 at 21:42

10 Answers10

219

Set the touch-action CSS property to none, which works even with passive event listeners:

touch-action: none;

Applying this property to an element will not trigger the default (scroll) behavior when the event is originating from that element.

John Weisz
  • 30,137
  • 13
  • 89
  • 132
  • 3
    That seems to also kill `touchstart` events so not useful if you want to recognize touch events but not allow scrolling. – Badrush Nov 19 '17 at 22:49
  • 19
    @Badrush -- Are you sure? I'm using this on my own "draggable" components, and `touchstart` fires just fine. – John Weisz Nov 20 '17 at 12:34
  • 6
    Works great for a canvas element (just put style="touch-action: none" right in the html). What could be simpler! Other solutions from this page failed for me. Thanks! – Yury Feb 04 '19 at 23:01
  • 3
    This won't prevent it from moving if the visitor doesn't do a mouse up. So basically, it can't be applied dynamically while dragging. – Alvaro Nov 17 '22 at 19:13
  • @Alvaro, in what situation would you need to dynamically apply it? [Here's how it appear to be expected to be used](https://jsgist.org/?src=96f9293f224c6354f62a658dac99db05) You already know the element you don't want to scroll so just mark that element as `touch-action: none`? – gman Apr 09 '23 at 17:39
50

Note: As pointed out in the comments by @nevf, this solution may no longer work (at least in Chrome) due to performance changes. The recommendation is to use touch-action which is also suggested by @JohnWeisz's answer.

Similar to the answer given by @Llepwryd, I used a combination of ontouchstart and ontouchmove to prevent scrolling when it is on a certain element.

Taken as-is from a project of mine:

window.blockMenuHeaderScroll = false;
$(window).on('touchstart', function(e)
{
    if ($(e.target).closest('#mobileMenuHeader').length == 1)
    {
        blockMenuHeaderScroll = true;
    }
});
$(window).on('touchend', function()
{
    blockMenuHeaderScroll = false;
});
$(window).on('touchmove', function(e)
{
    if (blockMenuHeaderScroll)
    {
        e.preventDefault();
    }
});

Essentially, what I am doing is listening on the touch start to see whether it begins on an element that is a child of another using jQuery .closest and allowing that to turn on/off the touch movement doing scrolling. The e.target refers to the element that the touch start begins with.

You want to prevent the default on the touch move event however you also need to clear your flag for this at the end of the touch event otherwise no touch scroll events will work.

This can be accomplished without jQuery however for my usage, I already had jQuery and didn't need to code something up to find whether the element has a particular parent.

Tested in Chrome on Android and an iPod Touch as of 2013-06-18

Turnerj
  • 4,258
  • 5
  • 35
  • 52
  • I found this worked for my needs `$(".carousel").on('touchstart', function (e) { e.preventDefault(); });` which prevents up and down scroll on my carousel :) – chris c Aug 30 '18 at 06:23
  • 4
    Chrome now ignore event.preventDefault() for touch events, so this won't work. See: https://developers.google.com/web/updates/2017/01/scrolling-intervention – nevf May 01 '19 at 06:42
  • 1
    Thank @nevf, I've added a note to my answer pointing that out and referring to JohnWeisz's answer for what seems to be the recommended approach. Glad to know my approach at least lasted a few years. – Turnerj May 01 '19 at 07:18
31

There is a little "hack" on CSS that also allows you to disable scrolling:

.lock-screen {
    height: 100%;
    overflow: hidden;
    width: 100%;
    position: fixed;
}

Adding that class to the body will prevent scrolling.

Mehdi
  • 408
  • 4
  • 12
  • 2
    This worked for me perfectly on iphone / ipad. Thanks! –  Dec 07 '16 at 16:01
  • This solution is the best. I tried both the accepted one and the most voted one. Both didn't worked. This one is clean and lightweight. – Murtaza Munshi Jul 07 '17 at 22:30
  • 1
    that works when we do not need scroll but `touch-action: none;` allows having a scroll that cannot be scrolled in touch devices; it is practical when `scrollbar-width` is none. – Amir2mi Jun 01 '21 at 15:36
20
document.addEventListener('touchstart', function(e) {e.preventDefault()}, false);
document.addEventListener('touchmove', function(e) {e.preventDefault()}, false);

This should prevent scrolling, but it will also break other touch events unless you define a custom way to handle them.

Raphael Rafatpanah
  • 19,082
  • 25
  • 92
  • 158
  • 5
    I had tried this with ontouchmove, which did disable scrolling, but I wasn't able to get it back. – Scott Hunter May 03 '13 at 01:26
  • 6
    There is one important thing missing: you need to set `{passive: false}` `document.addEventListener('touchstart', handleEndEventFn, { passive: false });` – dreampulse Sep 12 '17 at 11:53
  • I found that just disabling `touchmove` but instead of binding the eventlistener to the document I binded it directly to the canvas element helped prevent scrolling when using the canvas but allowed `touchstart` to work since I didn't prevent it. – Badrush Nov 19 '17 at 22:52
  • How do I get the touchmove back? I tried to call `document.removeEventListener('touchmove', handleFn)`, but it doesn't work. – newman Nov 06 '18 at 05:04
  • @newman To successfully remove it you have to have all parameters exactly as when you added it including the { passive: false } – Johncl Oct 02 '20 at 08:36
7

The ultimate solution would be setting overflow: hidden; on document.documentElement like so:

/* element is an HTML element You want catch the touch */
element.addEventListener('touchstart', function(e) {
    document.documentElement.style.overflow = 'hidden';
});

document.addEventListener('touchend', function(e) {
    document.documentElement.style.overflow = 'auto';
});

By setting overflow: hidden on start of touch it makes everything exceeding window hidden thus removing availability to scroll anything (no content to scroll).

After touchend the lock can be freed by setting overflow to auto (the default value).

It is better to append this to <html> because <body> may be used to do some styling, plus it can make children behave unexpectedly.

EDIT: About touch-action: none; - Safari doesn't support it according to MDN.

soanvig
  • 79
  • 1
  • 2
  • This worked for me on iPad (iOS 13) where I only want one item at a time to be moveable. e.preventDefault(); doesn’t seem to prevent the srceen from moving. – JScarry Apr 29 '20 at 15:50
2

I found that ev.stopPropagation(); worked for me.

micahblu
  • 4,924
  • 5
  • 27
  • 33
2

To my surprise, the "preventDefault()" method is working for me on latest Google Chrome (version 85) on iOS 13.7. It also works on Safari on the same device and also working on my Android 8.0 tablet. I am currently implemented it for 2D view on my site here: https://papercraft-maker.com

Phuocdh90
  • 51
  • 1
  • 5
1

try overflow hidden on the thing you don't want to scroll while touch event is happening. e.g set overflow hidden on Start and set it back to auto on end.

Did you try it ? I'd be interested to know if this would work.

document.addEventListener('ontouchstart', function(e) {
    document.body.style.overflow = "hidden";
}, false);

document.addEventListener('ontouchmove', function(e) {
    document.body.style.overflow = "auto";
}, false);
Woody
  • 7,578
  • 2
  • 21
  • 25
  • I'm not clear on how to use this; where/when do these calls get made? How does normal scrolling get restored once the "touch" is done (touchmove doesn't designate the END of the touch, just a movement WITHIN a touch, as I understand it). – Scott Hunter May 03 '13 at 13:46
  • normal scrolling would be restored by setting overflow back to auto. In effect you are removing the scrollbars and thus the scrollability of the body in this case. So you would need to reenable it when you are done with the gesture that the unwanted scrolling is affecting. Would that be ontouchend ? – Woody May 03 '13 at 15:10
  • this was my initial thought but it is not working on mobile – oldboy Dec 29 '20 at 03:18
0

this worked for me on iphone

$(".owl-carousel").on('touchstart', function (e) { 
            e.preventDefault();      
   });
Waqar
  • 163
  • 2
  • 12
0

the modern way (2022) of doing this is using pointer events as outlined here in the mozilla docs: https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events

Pointer events build on touchstart and other touch events and actually stop scroll events by default along with other improvements.

AGrush
  • 1,107
  • 13
  • 32