Last active
September 3, 2023 16:47
-
-
Save KonnorRogers/bf2b5e0356578263ebabf8799064f242 to your computer and use it in GitHub Desktop.
Automatically assign the proper "this" when using addEventListener
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
class BaseElement extends HTMLElement { | |
constructor () { | |
super() | |
/** | |
* @type {Map<(this: HTMLElement, evt: HTMLElementEventMap[keyof HTMLElementEventMap]) => any, {handleEvent: HTMLElementEventMap[keyof HTMLElementEventMap]}> | null} | |
*/ | |
this.__eventMap__ = null | |
} | |
/** | |
* @template K | |
* @param {K extends keyof HTMLElementEventMap ? keyof HTMLElementEventMap : never} type | |
* @param {K extends keyof HTMLElementEventMap ? ((this: HTMLElement, evt: HTMLElementEventMap[K]) => any) : never } callback | |
* @param {boolean | AddEventListenerOptions} [options] | |
* @returns {void} | |
*/ | |
addEventListener (type, callback, options) { | |
if (typeof callback !== "function") { | |
super.addEventListener(type, callback, options) | |
return | |
} | |
if (this.__eventMap__ == null) this.__eventMap__ = new Map() | |
/** @type {K extends keyof HTMLElementEventMap ? (this: HTMLElement, ev: HTMLElementEventMap[K]) => any : never} */ | |
// @ts-expect-error | |
let event = this.__eventMap__.get(callback) | |
if (event == null) { | |
const self = this | |
const toEvent = { | |
/** | |
* @param {HTMLElementEventMap[type]} evt | |
*/ | |
handleEvent (evt) { | |
/** @type {K extends keyof HTMLElementEventMap ? (this: HTMLElement, ev: HTMLElementEventMap[K]) => any : never} */ | |
// @ts-expect-error | |
callback.call(self, evt) | |
} | |
} | |
/** @type {K extends keyof HTMLElementEventMap ? (this: HTMLElement, ev: HTMLElementEventMap[K]) => any : never} */ | |
// @ts-expect-error | |
this.__eventMap__.set(callback, toEvent) | |
/** @type {K extends keyof HTMLElementEventMap ? (this: HTMLElement, ev: HTMLElementEventMap[K]) => any : never} */ | |
// @ts-expect-error | |
event = toEvent | |
} | |
// @ts-expect-error | |
super.addEventListener(type, event, options) | |
} | |
/** | |
* @param {Parameters<HTMLElement["removeEventListener"]>} args | |
* @returns {ReturnType<HTMLElement["removeEventListener"]>} | |
*/ | |
removeEventListener (...args) { | |
const [type, callback, options] = args | |
if (typeof callback !== "function" || this.__eventMap__ == null) { | |
super.removeEventListener(type, callback, options) | |
return | |
} | |
let event = this.__eventMap__.get(callback) | |
if (event == null) { | |
super.removeEventListener(type, callback, options) | |
return | |
} | |
// @ts-expect-error | |
super.removeEventListener(type, event, options) | |
} | |
} | |
// Usage | |
class MyEl extends BaseElement { | |
constructor () { | |
super() | |
this.addEventListener("click", this.handleClick) | |
} | |
handleClick (evt) { | |
// Should have proper "this"! | |
console.log({ this: this, evt }) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment