Created
April 9, 2019 21:02
-
-
Save AlienKevin/78ce3fcda0f8e530a111e229802978a2 to your computer and use it in GitHub Desktop.
Add same event listener to multiple simultaneous events that are fired at about the same time.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function addSimultaneousEventListeners(eventNames, callback, options) { | |
const defaultOptions = { | |
inOrder: true, // the events should be fired in the order specified in eventNames array or not | |
tolerance: 500, // the tolerance time between the first and the last event in milliseconds, | |
// you can rarely fire two events at the exact moment | |
} | |
// merge default options with user options | |
const mergedOptions = { | |
...defaultOptions, | |
...options | |
}; | |
const inOrder = mergedOptions.inOrder; | |
const tolerance = mergedOptions.tolerance; | |
eventNames = Object.freeze(eventNames); // make array immutable | |
let eventList = []; | |
eventNames.forEach((eventName) => { | |
document.addEventListener(eventName, listener); | |
}) | |
function listener(e) { | |
eventList.push(e); | |
// trim eventList size | |
if (eventList.length > eventNames.length) { | |
eventList = eventList.slice(eventList.length - eventNames.length); | |
} | |
// console.log("eventList: " + eventList.reduce((string, event) => { | |
// return string + event.type + "@" + Math.round(event.timeStamp) + ", "; | |
// }, "")); | |
const eventStack = eventNames.slice(); // copy array | |
if (eventList.length === eventNames.length) { | |
const firstEvent = eventList[0]; | |
for (let i = 0; i < eventList.length; i++) { | |
const currentEvent = eventList[i]; | |
let currentEventName; | |
if (inOrder) { | |
currentEventName = eventNames[i]; | |
} else { | |
const eventIndex = eventStack.indexOf(currentEvent.type); | |
if (eventIndex >= 0) { | |
currentEventName = currentEvent.type; | |
eventStack.splice(eventIndex, 1); | |
} else { | |
return; // not the right events fired | |
} | |
} | |
if (currentEvent.type !== currentEventName || !approximatelyEqual(firstEvent.timeStamp, currentEvent.timeStamp)) { | |
return; // not the right event type or both events are NOT fired at the same time | |
} | |
} | |
// eventList will be in order when inOrder is true | |
// otherwise it will be the order the user fired them | |
// you should always test for event types | |
callback(eventList); | |
} | |
} | |
// test if a and b are equal within the tolerance range | |
function approximatelyEqual(a, b) { | |
return (Math.abs(a - b) <= tolerance); | |
} | |
} | |
// Demo: detect keydown, click, and contextmenu fire at about the same time in that order | |
addSimultaneousEventListeners(["keydown", "click", "contextmenu"], // events registered | |
(eventList) => { // listener | |
console.log("you pressed a key, left clicked, and right clicked the document!"); | |
console.log("eventList: ", eventList); | |
}, | |
{ // options | |
inOrder: true | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment