A common thing we see with behaviors is lots of this.$foo.removeEventListener('bar', this.baz); in a behavior destroy() method. Which is the intended use case, but, its easy to forget one and they're a pain to maintain - its a chore.
Being able to automatically remove event listeners on behavior destroy() would be nice.
Another benefit of using an abort signal is that you could automatically remove anonymous functions, should you need to use those.
The easiest way would be to for each behavior to have a this.abortController and then every addEventListener would need { signal: this.abortController.signal } as its 3rd param, eg:
And behaviours somehow automagically appends { signal: this.abortController.signal } to the listener (also take into account that you may want { passive: true } or some other options. I don't know of a way to do this kind of fiddling with the methods/prototype of a DOM node only if its a named thing inside a behavior - @m4n1ok, @joecritch any idea?
This branch updates the existing getChild and getChildren methods (backwards compatible), adding on and off methods to the selection and children of the selection.
this.$btns = this.getChildren('btn'); // makes a collection using behavior's `this.getChildren('btn')`
// or
this.$btn = this.getChild('btn'); // makes a collection using behavior's `this.getChildren('btn')`
// or
this.$btns = this.getChildren(document.querySelector('button')) // makes a collection based on a single DOM node
// or
this.$btns = this.getChild(document.querySelectorAll('button')) // makes a collection based on a single DOM nodelist
// or
this.$btns = this.getChild(window) // makes a collection based on `window`
Where:
console.log(this.$btns); // NodeList(2) [button, button, on: ƒ, off: ƒ]
console.log(typeof this.$btns); // object
console.log(this.$btns[0]); // a DOM node (as as any nodelist)
And then you can add event listeners to a collection:
// and then
this.$btns.on('click', this.handleClick); // adds a click listener to all items in the collection with function `this.handleClick`
// or
this.$btns.on('click', () => {
console.log('hello world'); // adds a click listener to all items in the collection with anonymous function
});
// can also pass options
this.$btns.on('click', this.handleClick, { passive: true }); // adds a click listener to all items in the collection with function `this.handleClick` and `passive: true` option
// or select single items and add
this.$btns[0].on('click', this.handleClick); // adds a click listener to the first item in the collection with function `this.handleClick`
These will be be automatically cleaned up on behavior destroy().
(if you pass a custom abort controller signal as an option, it won't auto destroy)
And this would also be cleaned up on behavior destroy().
Should you also want to manually remove listeners you can:
this.$btns.off('click', this.handleClick); // removes all `click` listeners with the function `this.handleClick`
this.$btns.off('click'); // removes all `click` listeners, regardless of their associated functions
this.$btns.off(); // removes all event listeners, regardless of their type and associated function
this.$btns[0].off('click', this.handleClick); // removes the listener from just the first element
If you select an element that was previously in a selection, it will have on and off methods:
this.$btns = this.getChildren('btn');
console.log(typeof this.$node.querySelector('button').on); // function
Which means you can do:
this.$btns.on('click', (event) => {
event.currentTarget.off('click); // will remove all 'click' listeners from the clicked button
});
Listeners won't be added twice:
this.$btns.on('click', this.handleClick);
this.$btns.on('click', this.handleClick); // won't add the handler twice
Using on and off as not to confuse with addEventListener and removeEventListener. On individual nodes, native addEventListener and removeEventListener are still available.
[x] check to see if a getChild found element that then becomes a plugin container, eg. a Splide container, that the plugin on and off methods correctly overwrite the behavior collection methods
so AbortController support with addEventListener isn't as widely support as AbortController with fetch. But, there is a simple polyfill to match support, which I'm not going to include in behaviors but will mention in the wiki.
Use the
AbortController()
's ability to remove multiple event listeners onabort()
to auto remove event listeners on behaviordestroy()
.See this article.
A common thing we see with behaviors is lots of
this.$foo.removeEventListener('bar', this.baz);
in a behaviordestroy()
method. Which is the intended use case, but, its easy to forget one and they're a pain to maintain - its a chore.Being able to automatically remove event listeners on behavior
destroy()
would be nice. Another benefit of using an abort signal is that you could automatically remove anonymous functions, should you need to use those.The easiest way would be to for each behavior to have a
this.abortController
and then everyaddEventListener
would need{ signal: this.abortController.signal }
as its 3rd param, eg:But, as @kylegoines points out - developers probably won't notice they can use it.
The nicest thing we could have would be:
And behaviours somehow automagically appends
{ signal: this.abortController.signal }
to the listener (also take into account that you may want{ passive: true }
or some other options. I don't know of a way to do this kind of fiddling with the methods/prototype of a DOM node only if its a named thing inside a behavior - @m4n1ok, @joecritch any idea?This branch updates the existing
getChild
andgetChildren
methods (backwards compatible), addingon
andoff
methods to the selection and children of the selection.Where:
And then you can add event listeners to a collection:
These will be be automatically cleaned up on behavior
destroy()
. (if you pass a custom abort controller signal as an option, it won't auto destroy)You could also:
And this would also be cleaned up on behavior
destroy()
.Should you also want to manually remove listeners you can:
If you select an element that was previously in a selection, it will have
on
andoff
methods:Which means you can do:
Listeners won't be added twice:
Using
on
andoff
as not to confuse withaddEventListener
andremoveEventListener
. On individual nodes, nativeaddEventListener
andremoveEventListener
are still available.To do:
getChild
found element that then becomes a plugin container, eg. a Splide container, that the pluginon
andoff
methods correctly overwrite the behavior collection methods