ftlabs / fastclick

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

iOS 7 - cancel tap when stopping a scroll #227

Open ebgraham opened 10 years ago

ebgraham commented 10 years ago

I have page embedded in a UIWebView on iOS 7. When a tap happens to stop a momentum scroll, that onClick event is being registered. The default iOS behavior is to ignore that onClick.

philipgiuliani commented 10 years ago

+1 i have the same issue :/

matthew-andrews commented 10 years ago

Is this exclusive to the UIWebView? Or is it reproducible in Safari as well?

philipgiuliani commented 10 years ago

Its in Safari aswell. When i scroll and i click on the "scrolling" webview, i get the onClick event instead of stopping it from scrolling.

I think it has something to do with https://github.com/ftlabs/fastclick/issues/42

Edit: I have webkit-overflow-scroll: touch in another div container with overflow-y: scroll. Could it be that its just preventing the click if its in the body?

I found this line

if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
   return true;
}

and i think that pageX won't change in a scrollable div.

nolanw commented 10 years ago

Doesn't work on the body either. The issue is that onTouchStart twice sets fastClickLastScrollTop: once when setting off inertial scroll, and again on the touch to stop scrolling. For this second event, onTouchEnd compares the scrollTop from when we tapped to stop scrolling with the current scrollTop, sees they are identical (it was just a tap), and assumes no scrolling was done.

It's possible that on iOS the scroll event might be helpful here, as apparently it'll fire after inertial scrolling is done (on iOS 7 at least).

pcompassion commented 10 years ago

same here with UIWebView, and iOS7.1

I have webkit-overflow-scroll: touch

jameson5555 commented 10 years ago

I'm having the same sort of issue on iOS 7.1.2 in a div with -webkit-overflow-scroll: touch on it.

While scrolling, if I tap to continue scrolling, the page often flickers up and down quickly. If I tap to stop scrolling, the page also jumps up and down, but more slowly. Disabling fastclick gets rid of the issue. I'm trying a temporary fix that puts pointer-events: none; on the page during a scroll event, but it's not as smooth as I'd like it to be.

cshfang commented 10 years ago

+1

JZ-at-TP commented 10 years ago

This is causing major issues, it the click goes to the wrong element which causes incorrect data to be saved. Any ETA on this?

justinsherwood commented 10 years ago

+1

lauri865 commented 10 years ago

Any luck with a fix, anyone?

s4y commented 10 years ago

How about something along these lines?

addEventListener('touchstart', function(e){
    var scrolled = false;
    function scrollHandler(){
        scrolled = true;
    }
    addEventListener('scroll', scrollHandler);
    setTimeout(function() {
        removeEventListener('scroll', scrollHandler);
        if (scrolled) return;
        console.log('touched!');
    }, 0);
});
Cinamonas commented 10 years ago

Apparently, FastClick.prototype.updateScrollParent is broken. In my case, it does not correctly identify scroll parent. Because of this, taps that should stop scrolling are not ignored.

The method is not private, so for the time being it is possible to override it for your specific case.

brianephraim commented 10 years ago

Try extending Fastclick's touchend handler with a setTimeout wrapper. Timeout of zero didn't work, but 10 seems to do the trick. Tested in IOS 7.1 Simulator for iPhone and iPad.

var old_onTouchEnd = FastClick.prototype.onTouchEnd;
FastClick.prototype.onTouchEnd = function(e){
    var args = arguments;
    var self = this;
    setTimeout(function() {
        old_onTouchEnd.apply(self,args)
    },10);
};
ruffle1986 commented 9 years ago

According to my experiences, it's not a good idea to make it async, especially if you want to add autofocus on a form element when the user opens (for example) your login form. It won't work because you brake the call stack. iOS only allows the autofocus if it's followed by a user interaction event in the same message queue.

cmwelsh commented 9 years ago

This bug makes fastclick unusable for my iOS project.

limitedmage commented 9 years ago

I'm seeing this same issue on a Cordova app in iOS 8. @defualt's workaround did not work for me.

OliverJAsh commented 9 years ago

I am seeing the same issue.

How can we fix this? As far as FastClick is concerned, the action did not cause scroll, so the user's intention must be to click. How could we detect that the user’s intent was not to click, but rather to stop the scroll?

OliverJAsh commented 9 years ago

I'm testing a workaround for this in iOS 9:

var layer = document.body;
var fastClick;

var initFastClick = function () {
  fastClick = new FastClick(layer);
};

var remove = function () {
  console.log('remove');
  fastClick.destroy();
};

var add = function () {
  console.log('add');
  initFastClick();
};

var timeoutId;
window.addEventListener('scroll', function () {
  remove();
  if (timeoutId) {
    window.clearTimeout(timeoutId);
  }
  timeoutId = window.setTimeout(add, 300);
});

initFastClick();

Not sure how it performs with the amount of timeouts I'm creating. Ideally this would be integrated into the library. Also not sure how it performs on older versions of iOS.

ArthurClemens commented 8 years ago

@OliverJAsh It depends which element is scrolling, so it is difficult to generalise this. I encountered the problem in Polythene where a header panel has scrollable conten, and window will not emit scroll events when the panel contents is scrolled.

My adaptation lets the panel emit a scroll event instead of the window object.