- 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