Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
JedWatson opened this issue Jun 17, 2015 · 9 comments
Closed

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

JedWatson opened this issue Jun 17, 2015 · 9 comments
Labels

Comments

@JedWatson
Copy link
Owner

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
Copy link
Owner Author

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
Copy link
Owner Author

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!

nmn added a commit to nmn/react-tappable that referenced this issue Jul 2, 2015
* 'master' of https://github.com/JedWatson/react-tappable: (71 commits)
  Exposing touchStyles object
  Adding cancelTap method
  Documenting style prop
  persist event for onTap
  v0.5.2
  Updating changelog
  Updating build
  Adding activeDelay to docs on Readme
  Adding activeDelay prop to delay hilighting when required
  Cleanup
  Updating babel and eslint
  v0.5.1
  Updating changelog
  Updating build
  Prevent opportunity for competing renders to break CSS transitions
  Detecting momentum scroll end event, fixes JedWatson#38
  v0.5.0
  Updating changelog for release
  Updating example build
  Updating common.js in example dist
  ...
@steverandy
Copy link

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
Copy link
Owner Author

@steverandy did you find a solution to that?

@steverandy
Copy link

@JedWatson No luck.

@slorber might have a solution #55

@slorber
Copy link
Contributor

slorber commented Oct 12, 2015

@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
Copy link

TNT-RoX commented Nov 3, 2015

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
Copy link
Contributor

slorber commented Nov 3, 2015

@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
Copy link

aZolo77 commented Aug 21, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants