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

Improve reliability of mouse event from touch interaction in polyfill #27

Open
rafgraph opened this issue Nov 15, 2016 · 1 comment
Open

Comments

@rafgraph
Copy link

rafgraph commented Nov 15, 2016

Nice polyfill. I noticed that you set lastTouchTime = Date.now() on each touch event and then check if the mouse event comes in within 1000ms of the last touch event to determine if the mouse event was from a touch interaction. This works most of the time (I used this technique in the-listener), except when the touch event starts a blocking process that takes longer than 1000ms. The mouse event will come in after the 1000ms and won't be recognized as originating from a touch interaction.

To fix this, I used a setTimeout to take advantage of the browser's multithreading capabilities.

var recentTouch = false;
var touchTimerID = null;
function touchHandler(event) {
  recentTouch = true;
  // only have one timer running at a time, otherwise recentTouch could be set to false
  // from an earlier touch while the most recent touch event's timer is still running, so
  // clear the timer when a touch event comes in before the previous event's timer ends
  if (touchTimerID !== null) window.clearTimeout(touchTimerID);
  touchTimerID =  window.setTimeout(function() {
    touchTimerID = null;
    recentTouch = false;
  // 1000ms may be longer than is needed with this technique
  }, 600)
}

And then use recentTouch to determine if a mouse event is from a touch interaction.

Note that in the case of a long blocking process from a touch event, the browser will push the corresponding mouse event onto the queue when it should be executed (anywhere from a few ms to 300+), and then push the timeout function onto the queue after the timer expires. This results in the mouse event being pushed onto the callstack and executed before the timeout function, even if it isn't executed until several seconds later, so it's recognized as a mouse event from a touch interaction.

Also, if subsequent touch events occur while the blocking process is running, the browser will push the touch events onto the queue when the touch happens, and if one of them is in queue before the previous touch event's timer expires, it will execute before the timeout's function, and, this is the key part, the call to clearTimeout(touchTimerID) will remove the timeout's function from the queue even after the timer has finished (I did some testing for this scenario when I came up with the solution because I wasn't sure, but it appears to be the case).

Hope you find this useful.

@rafgraph
Copy link
Author

rafgraph commented Nov 15, 2016

Also, in my experience you only need to set a timer on touchend, as well as keep track of if there is a touch point currently on the screen as mouse events from touch interactions only fire after touchend, and, on Chrome on Android and Windows, on long press while still touching the screen (the latter I think is to simulate mouse hover functionality, but you would know better than I would). You could use an extra long timer set on touchstart to ignore the mouse events from a long press, but to me it made more sense to just keep track of if there is a touch point currently on the screen.

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

No branches or pull requests

1 participant