JedWatson / react-tappable

Tappable component for React
http://jedwatson.github.io/react-tappable/
MIT License
862 stars 90 forks source link

Touch-to-stop-scroll also fires the tap event #38

Closed JedWatson closed 9 years ago

JedWatson commented 9 years ago

In native mobile apps, touching a scrollable container while momentum scrolling is in effect will immediately stop the scroll momentum, and prevent any tap events from firing on the elements inside the container.

(by scrollable container, I mean an element with -webkit-overflow-scrolling:touch applied)

Currently, react-tappable doesn't know that a momentum scroll is in progress, so it will treat the interaction like a normal tap event (which leads to a really broken user experience).

I'm currently investigating whether we can detect scroll events in any way, and block the tap correctly. No idea (yet) how I'm actually going to fix this though.

JedWatson commented 9 years ago

Progress update: scroll events don't fire during momentum scrolling, only (what I am going to call) "touch-tracked" scrolling. fastclick seems to implement a solution for this by detecting changes to the scrollTop in parent elements, I'm going to see if I have any luck porting that.

JedWatson commented 9 years ago

I ended up being able to fix this by calculating the current scroll position of the tappable's scroll parents on touchStart and comparing it to the scroll position after touchEnd.

Initially I used a setTimeout to allow the DOM elements to update (basically, no scroll event fires during momentum scrolling, but the scrollTop is updated when momentum scrolling is halted), however the more robust solution turned out to be checking the final scroll offset and firing the onTap event in the callback of the tappable's setState({ active: false }).

I can't find any issues with this approach on iOS, and happily it seems to also resolve an edge case where the tappable setState would (rarely) conflict with TouchstoneJS transitions, breaking the CSSTransitionGroup animation.

Win!

steverandy commented 9 years ago

On iOS, the tap event still fires when I do touch-to-stop-scroll on the <body> (not using -webkit-overflow-scrolling:touch).

Although the scrolling has higher friction without -webkit-overflow-scrolling:touch, I still find it annoying by the accidental taps.

JedWatson commented 9 years ago

@steverandy did you find a solution to that?

steverandy commented 9 years ago

@JedWatson No luck.

@slorber might have a solution https://github.com/JedWatson/react-tappable/issues/55

slorber commented 9 years ago

@steverandy I'm not sure to understand exactly your problem.

Isn't the @JedWatson solution to prevent tap events to fire in case of momentum scrolling stopped? Your problem only happens to body?

My issue is probably a duplicate of this one and also references FastClick implementation so not sure it brings anything to the table

TNT-RoX commented 9 years ago

Hi, maybe my 2 cents can help here. I don't use react or any other Dom bloater, so I'll drop some javascript to show how this can be resolved.

The issue will be persistent on both ios and android devices. The reason is momentum scrolling is a native function, the os waits 200ms to see if preventdefault has been set. If not, all touch events to the dom is suspended and the device handles the native scroll.

So the solution is quite simple, catch the tap within 200ms. Here is some code for how I catch a tap on a momentum scroll surface.

element.addEventListener('touchstart', function(e)) {
    var target = this;
    var handler = function(evt) {
        e.preventDefault();
        e.stopImmediatePropagation();
        evt.preventDefault();
        evt.stopImmediatePropagation();
        /*
         Your tap event handler code here
         */
    };
    target.addEventListner('touchend', handler);
    setTimeout(function() {
        target.removeEventListener('touchend', handler)
    }, 150);
});

Hope this helps :) happy coding..

slorber commented 9 years ago

@TNT-RoX The problem is not really to catch a tap during a momentum scroll, but rather to know that a tap has been done during a momentum scroll.

IMHO your code only permits to catch a tap, but does not permit to know weither or not this tap happens during a momentum scroll

aZolo77 commented 4 years ago

In native mobile apps, touching a scrollable container while momentum scrolling is in effect will immediately stop the scroll momentum, and prevent any tap events from firing on the elements inside the container.

(by scrollable container, I mean an element with -webkit-overflow-scrolling:touch applied)

Currently, react-tappable doesn't know that a momentum scroll is in progress, so it will treat the interaction like a normal tap event (which leads to a really broken user experience).

I'm currently investigating whether we can detect scroll events in any way, and block the tap correctly. No idea (yet) how I'm actually going to fix this though.

Hi! Did you fix this issue? I ask cause I want exactly opposite. In my app I need my users to have an opportunity to tap while scrolling, but as you've said first tap stops the scroll and only after user can do what he wanted. May be if u didnt fix that I can use yr library. Or may be I can use version before this exact fix.