event.js 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. const listenerRegistry = new WeakMap();
  2. const {addEventListener, removeEventListener} = EventTarget.prototype;
  3. // Register event listeners to a key object
  4. // listeners: array of listener definitions;
  5. // - each definition must be a flat array of event target and the arguments
  6. // used to call addEventListener() on the target
  7. export function registerListeners(keyObj, listeners) {
  8. let registered = listenerRegistry.get(keyObj);
  9. if (!registered) {
  10. registered = [];
  11. listenerRegistry.set(keyObj, registered);
  12. }
  13. listeners.forEach((listener) => {
  14. addEventListener.call(...listener);
  15. registered.push(listener);
  16. });
  17. }
  18. export function unregisterListeners(keyObj) {
  19. let listeners = listenerRegistry.get(keyObj);
  20. if (!listeners) {
  21. return;
  22. }
  23. listeners.forEach((listener) => {
  24. removeEventListener.call(...listener);
  25. });
  26. listenerRegistry.delete(keyObj);
  27. }
  28. // Event.composedPath() polyfill for Edge
  29. // based on https://gist.github.com/kleinfreund/e9787d73776c0e3750dcfcdc89f100ec
  30. if (!Event.prototype.composedPath) {
  31. const getComposedPath = (node, path = []) => {
  32. path.push(node);
  33. let parent;
  34. if (node.parentNode) {
  35. parent = node.parentNode;
  36. } else if (node.host) { // ShadowRoot
  37. parent = node.host;
  38. } else if (node.defaultView) { // Document
  39. parent = node.defaultView;
  40. }
  41. return parent ? getComposedPath(parent, path) : path;
  42. };
  43. Event.prototype.composedPath = function () {
  44. return getComposedPath(this.target);
  45. };
  46. }
  47. function findFromPath(path, criteria, currentTarget, index = 0) {
  48. const el = path[index];
  49. if (criteria(el)) {
  50. return el;
  51. } else if (el === currentTarget || !el.parentElement) {
  52. // stop when reaching currentTarget or <html>
  53. return;
  54. }
  55. return findFromPath(path, criteria, currentTarget, index + 1);
  56. }
  57. // Search for the actual target of a delegated event
  58. export function findElementInEventPath(ev, selector) {
  59. const criteria = typeof selector === 'function' ? selector : el => el.matches(selector);
  60. return findFromPath(ev.composedPath(), criteria, ev.currentTarget);
  61. }