ember-on-helper
Version:
{{on eventTarget eventName someAction}} template helper
138 lines (128 loc) • 3.72 kB
JavaScript
/* eslint no-param-reassign: "off" */
import { assert } from '@ember/debug';
import { DEBUG } from '@glimmer/env';
/**
* Internet Explorer 11 does not support `once` and also does not support
* passing `eventOptions`. In some situations it then throws a weird script
* error, like:
*
* ```
* Could not complete the operation due to error 80020101
* ```
*
* This flag determines, whether `{ once: true }` and thus also event options in
* general are supported.
*/
export const SUPPORTS_EVENT_OPTIONS = (() => {
try {
const div = document.createElement('div');
let counter = 0;
div.addEventListener('click', () => counter++, { once: true });
let event;
if (typeof Event === 'function') {
event = new Event('click');
} else {
event = document.createEvent('Event');
event.initEvent('click', true, true);
}
div.dispatchEvent(event);
div.dispatchEvent(event);
return counter === 1;
} catch (error) {
return false;
}
})();
/**
* Registers an event for an `element` that is called exactly once and then
* unregistered again. This is effectively a polyfill for `{ once: true }`.
*
* It also accepts a fourth optional argument `useCapture`, that will be passed
* through to `addEventListener`.
*
* @param {Element} element
* @param {string} eventName
* @param {Function} callback
* @param {boolean} [useCapture=false]
*/
export function addEventListenerOnce(
element,
eventName,
callback,
useCapture = false
) {
function listener() {
element.removeEventListener(eventName, listener, useCapture);
callback();
}
element.addEventListener(eventName, listener, useCapture);
}
/**
* Safely invokes `addEventListener` for IE11 and also polyfills the
* `{ once: true }` and `{ capture: true }` options.
*
* All other options are discarded for IE11. Currently this is only `passive`.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
*
* @param {Element} element
* @param {string} eventName
* @param {Function} callback
* @param {object} [eventOptions]
*/
export function addEventListener(element, eventName, callback, eventOptions) {
const _callback =
DEBUG && eventOptions && eventOptions.passive
? function(event) {
event.preventDefault = () => {
assert(
`ember-on-helper: You marked this listener as 'passive', meaning that you must not call 'event.preventDefault()'.`
);
};
return callback.call(this, event);
}
: callback;
if (SUPPORTS_EVENT_OPTIONS) {
element.addEventListener(eventName, _callback, eventOptions);
} else if (eventOptions && eventOptions.once) {
addEventListenerOnce(
element,
eventName,
_callback,
Boolean(eventOptions.capture)
);
} else {
element.addEventListener(
eventName,
_callback,
Boolean(eventOptions && eventOptions.capture)
);
}
}
/**
* Since the same `capture` event option that was used to add the event listener
* needs to be used when removing the listener, it needs to be polyfilled as
* `useCapture` for IE11.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
*
* @param {Element} element
* @param {string} eventName
* @param {Function} callback
* @param {object} [eventOptions]
*/
export function removeEventListener(
element,
eventName,
callback,
eventOptions
) {
if (SUPPORTS_EVENT_OPTIONS) {
element.removeEventListener(eventName, callback, eventOptions);
} else {
element.removeEventListener(
eventName,
callback,
Boolean(eventOptions && eventOptions.capture)
);
}
}