0

I have some JavaScript to handle touch events on a web page and watch for gestures that are predominantly horizontal. When the user begins to swipe horizontally, I want the effects of their gesture to begin, but when they swipe vertically, the page should scroll naturally as it would otherwise.

While horizontal gestures do get the desired effect right now, they also still seem to cause vertical scroll for the amount the touch moves up or down, even while the majority of the movement is horizontal. How would I fix this to prevent vertical scrolling when swiping predominantly horizontally? I've used both preventDefault() and stopPropagation() to no effect. I imagine there's something really obvious I'm missing, but I just can't seem to find it.

I've tried my best to search for similar questions, but my search queries are yet to return anything that works.

Note: I don't want to stop vertical scrolling altogether, and even if there's an X-value change in the gesture, the page should still scroll if the Y-change is greater. I only want the vertical scroll to lock if the X-change is greater than the Y-change (which is what my code should do right now).

Update: I hadn't thought to retrieve console logging from a mobile device. On @tushar-shukla's suggestion, I checked it, discovering the following error:

[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See <URL>

Here is a watered-down version of my code (and a JSFiddle, if that's more your cup of tea):

var startX = undefined;
var startY = undefined;

function touchmove(ev) {
    var e = ev.originalEvent;

    // Set the initial start position if not already. Could also be done on touchstart
    if (startX===undefined) { startX=e.touches[0].pageX; }
    if (startY===undefined) { startY=e.touches[0].pageY; }

    // Calculate the change or displacement from the start.
    changeX = e.touches[0].pageX-startX;
    changeY = e.touches[0].pageY-startY;

    // If the change horizontally is greater than the change vertically...
    if (Math.abs(changeX)>Math.abs(changeY)) {

        // Do something with the gesture

        // Prevent vertical scrolling; Apparently this isn't doing it:
        ev.preventDefault();
        ev.stopPropagation();
        e.preventDefault();
        e.stopPropagation();

    }
}

$(document).on("touchmove",touchmove);
Laef
  • 1,086
  • 2
  • 11
  • 27

2 Answers2

1

After hooking up my mobile device to Chrome, I retrieved the console logging, containing the following repeated error:

[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See <URL>

This was the clue I needed to solve the problem. Modern mobile browsers have passive and active event listeners, where active ones can prevent the default action for the event and passive ones can't. This is a performance optimisation for things like touch gestures, which need to run as smoothly as possible, even on bulky web pages.

JavaScript uses passive event listeners automatically for events such as touchmove. While JavaScript offers a parameter when attaching the event listener to change this behaviour, jQuery, unfortunately, does not.

This means, of course, that the problem can be easily remedied by replacing:

$(document).on("touchmove",touchmove);

In my code with:

document.addEventListener('touchmove', pageswiping.touchmove, {passive: false});

In this case, {passive: false} makes it clear that the event listener is an active one, not a passive one.

Laef
  • 1,086
  • 2
  • 11
  • 27
0

If I understood it correctly, you need to STOP VERTICAL SCROLL (which is unintended) when user is scrolling (or trying to) horizontally.

I haven't tested it yet but may be this can work.

var startX;
$(document).on('touchstart', function(e) {
  startX = e.originalEvent.touches[0].clientX;
});

$(document).on('touchmove', function(e) {
  var endX = e.originalEvent.changedTouches[0].clientX;
  if (startX !== endX) {
    console.log('attempting horizontal scroll');
    e.preventDefault();
  }
});
Tushar Shukla
  • 5,666
  • 2
  • 27
  • 41
  • I'm afraid this is already what I'm attempting, just in fewer lines (so well done there!). It seems that `e.preventDefault()` is not working at stopping the scroll. Please do check your code on a mobile device, but as best as I can see, this doesn't do any better... :/ Also note that you'll have to check if the X-change is greater than the Y-change, as usually gestures intended to scroll are not perfectly up-down. – Laef Aug 17 '18 at 04:22
  • 1
    I'm afraid I won't be able to test now but can you try `touch-action: none;` or `position: fixed;` and `overflow: hidden;` Also, notice that I have captured start event via `touchstart` event for precision, may be that'll do some good. As far as `preventDefault` is concerned, it should have done the job but since your'e saying that its not working, is there any error in `console.log()`? – Tushar Shukla Aug 17 '18 at 06:01
  • Ah, thank you for suggesting checking the console logging... I really ought to have checked that earlier, but didn't have a mobile device handy either. – Laef Aug 18 '18 at 00:16