- From: Andrea Giammarchi <notifications@github.com>
- Date: Tue, 27 Jun 2017 16:11:02 -0700
- To: whatwg/dom <dom@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <whatwg/dom/issues/472@github.com>
Coming from [this thread](https://github.com/whatwg/dom/issues/208) without the intent to go off topic in there, but since it's being considered to add properties to the listener options I'd like to propose the following one: ```js el.addEventListener(type, anyCallback, {context: anyObject}); ``` The reason is: it should always be possible, holding a reference to the element, the callback, and the context, to also **remove** the listener at any time: ```js el.removeEventListener(type, anyCallback, {context: anyObject}); ``` ### Rationale / Goal While memory consumption wise there won't probably be any huge improvement, it is still a very common mistake to assume that if you add a callback listener as such: ```js el.addEventListener(type, anyCallback.bind(anyObject)); ``` it is later on possible to remove it in some way: ```js // fail el.removeEventListener(type, anyCallback.bind(anyObject)); // also fail el.addEventListener(type, anyCallback); ``` ### Behavior * if the callback with the specified context has not been set already, add the listener and once invoked do: `callback.call(context, event)` * if the callback is known, and the context was previously used, do nothing * if the callback is known, and the context is different, add the listener and once invoked do `callback.call(otherContext, event)` Same logic would work to `removeEventListener` too. ### Implementation The following code is just a Proof Of Concept. It's not a polyfill or an implementation, it just shows the logic behind this feature. ```js (() => { const elwm = new WeakMap; Element.prototype.addEventListener = function (type, eventListener, options) { if (!elwm.has(this)) { elwm.set(this, Object.create(null)); } const ref = elwm.get(this); if (!(type in ref)) { ref[type] = { listeners: [], contextes: [] }; } const handler = ref[type]; let i = handler.listeners.indexOf(eventListener); if (i < 0) { i = handler.listeners.push(eventListener) - 1; handler.contextes[i] = []; } // if context is not provided (or no options at all) // the list of context will have one `null` value // this preservers the order in which listeners are added const context = options.context || null; if (!handler.contextes[i].includes(context)) { handler.contextes[i].push(context); } }; Element.prototype.dispatchEvent = function (event) { if (elwm.has(this)) { const ref = elwm.get(this); if (event.type in ref) { const handler = ref[type]; handler.listeners.forEach((listener, i) => { // invoke the listener callback // and use the context only if not null // if null, invokes it with the default context if (typeof listener === 'function') { handler.contextes[i].forEach(context => // which is the element node for callbacks listener.call(context || this, event) ); } else if ('handleEvent' in listener) { handler.contextes[i].forEach(context => // and the object with the method otherwise listener.handleEvent.call(context || listener, event) ); } }); } } }; })(); ``` Thanks in advance for eventually considering this proposal/idea. -- You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub: https://github.com/whatwg/dom/issues/472
Received on Tuesday, 27 June 2017 23:11:50 UTC