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.