ftlabs / fastclick

Polyfill to remove click delays on browsers with touch UIs
MIT License
18.66k stars 3.22k forks source link

Ghost Clicks After Page Transition #130

Open csarsam opened 11 years ago

csarsam commented 11 years ago

I'm working on an Android Phonegap application that I'm testing on a 2.3 device and on emulators. The structure is essentially a menu of divs:

<a><div>Menu Entry</div></a>
<a><div>Menu Entry</div></a>
<a><div>Menu Entry</div></a>

Though the URL starts loading quickly using Fastclick, a second click event fires after the Fastclick one. If it fires after the new page has loaded, it selects one of the menu items from the new page and thus that gets loaded. Placing event.preventDefault() on line 363 of fastclick.js, in the onTouchStart function, stops the second click event from firing but also disables scrolling.

Instantiating Fastclick on just the divs instead of the document body allows for scrolling anwhere outside the divs, but because the menu takes up around 90% of the screen it isn't really an acceptable solution.

Is there a way I can use Fastclick in this situation while preventing ghost clicks and not disrupting scrolling, or is this not a situation I should be using it in?

criminogene commented 11 years ago

+1! same problem here only on android browser (iOs browser are ok)! The ghost clicks are really annoying: http://agricolascorer.free.fr/

guillerodriguez commented 11 years ago

This is probably the same as https://github.com/ftlabs/fastclick/issues/146.

selwin commented 11 years ago

Here's an explanation of how Google's Fast Button handle ghost clicks. I don't know JS/fastclick well enough to write a patch, but hopefully this link can be of use:

https://developers.google.com/mobile/articles/fast_buttons#ghost

atuttle commented 11 years ago

My understanding is that when a tap occurs, two events are fired: the touch event that FastClick listens for (which is broadcast immediately), and 300ms later the click event that is resulting in this "ghost click" experience. Since you're listening for the touch event, you can't cancel the click event (they are distinct events).

The problem is that the click event doesn't fire for the element that was touched, it fires for whatever element is at the touch [x,y] coordinates after the 300ms delay has passed. If the target of both events were the same element, then you could simply listen for both events and ignore the click:

$('.fastclick').on('tap,click', function(e){
    if (e.type === 'click') return;
    //now your touch handler code: ...
});

Indeed, this approach works if your touch-event handler doesn't cause the page to move or any new elements to be displayed over the same [x,y] coordinates.

But if the touch event handler causes a transition (e.g. scroll to a different area of the page, open a new page, show an overlay or dialog window, etc) and this is completed before the 300ms delay completes, and there is a new element under the [x,y] coordinates that were touched then the click event runs for this new element. If this new element has a click handler, then you see a "ghost click."

The only solution I've found thus far is simply to suffer the 300ms delay and use click events when the resulting action could potentially cause a "ghost click."

criminogene commented 11 years ago

Thanks for your answer, now I understand why it doesn't work for me: I would like to open a popup dialog at the exact position where the click fires. Fastclick works fine on iOS but unfortunately Android fails to handle this case. To solve the issue, I don't load fastclick lib on android (but I suffer the 300ms delay :() Hope Android will fix that.

richotaylor commented 10 years ago

What I found in our project was....

  1. We are using fastclick on a button that gets disabled via javascript on touch / click (until we render our new page)
  2. Because the button was disabled when the second / real click came through the fastclick code, the click didn't get cancelled since the underlying target was disabled. This is for some reason intentional in fastclick, see the "needsClick" function.

I was able to work around this by simply not disabling the button on click any longer, it didn't affect the usability in our case. Just though I would mention it in case this same case was haunting someone else.

Thanks for the hints atuttle.

Zambonilli commented 10 years ago

I was seeing this behavior after switching pages using window.location. Which obviously is a nightmare because the next page's x,y coordinates have completely different elements. This mainly affected form inputs which would mysteriously gain focus on one device but not another or when in landscape vs portrait. Ultimately, we ended up registering a setTimeout(function(){$input1.blur();},301) on dom loaded. This looks to fix the random focus but is fugly.

scooterlord commented 10 years ago

The url user selwin looks really helpful. Will this be implemented in the plugin any time soon? It would help heaps! I am encountering the same issue!