JSFiddle - React, Tailwind, and code Playground
HTML
<p>This event model adds 3 properties to <code>options</code> argument of the native model.</p>
<ol>
<li><code>context</code>: binds <code>this</code> to the handler function</li>
<li><code>delegatee</code> passes a selector for event delegation</li>
<li><code>data</code> adds <code>data</code> property to <code>event</code> object</li>
</ol>
<p><strong>The model is a heavily simplified example only, don't use it in production.</strong></p>
<hr>
<h2>Testing section</h2>
<button id="but">Button</button>
CSS
code {
font-size: 1.25em;
color: red;
font-weight: bold;
}
JavaScript
const events = {
// Keeps track of the attached events
tracked: new WeakMap(),
// Attaches an event
attach (target, type, handler, options = {}) {
const context = options.context || target,
capture = !!options.capture || false,
once = !!options.once || false,
passive = !!options.passive || true,
delegatee = (typeof options.delegatee === 'string')
? options.delegatee : null,
// The actual event listener
fn = e => {
if (options.hasOwnProperty('data')) {
// Add data property to event object
e.data = options.data;
}
if (delegatee) {
// Delegated event, quit if the delegatee doesn't exist
if (!e.target.closest(delegatee)) {return;}
}
// Call the dev defined handler function
handler.call(context, e);
// Untrack a one-off event
if (once) {
// Untrack a one-off event
this.detach(target, type, handler, capture);
}
};
target.addEventListener(type, fn, {capture, once, passive});
if (!this.tracked.has(target)) {
this.tracked.set(target, []);
}
this.tracked.get(target).push({type, fn, handler, capture});
},
// Detaches an event
detach (target, type, handler, capture = false) {
const events = this.tracked.get(target);
if (!events) {return;} // Quit, target not found
const eIndex = events.findIndex(e =>
e.type === type &&
e.handler === handler &&
e.capture === capture
);
if (eIndex < 0) {return;} // Quit, event was not found
const event = events[eIndex];
target.removeEventListener(event.type, event.fn, event.capture);
events.splice(eIndex, 1);
if (events.length < 1) {
// No events left on this element, remove it from the map
this.tracked.delete(target);
}
}
}
// Testing ...
const but = document.querySelector('button'),
ctxObject = {
prop: 'Context object'
},
handler = function (e) {
...