问题
How do I remove the click listener I bound to window
in the constructor
below? I need it to listen on window
, and I need access to the button instance inside it.
class MyEl extends HTMLButtonElement {
constructor() {
super();
this.clickCount = 0;
window.addEventListener('click', this.clickHandler.bind(this));
}
clickHandler(e) {
if (e.target === this) {
this.textContent = `clicked ${++this.clickCount} times`;
window.removeEventListener('click', this.clickHandler);
}
}
disconnectedCallback() {
window.removeEventListener('click', this.clickHandler);
}
}
customElements.define('my-el', MyEl, { extends: 'button' });
<button is="my-el" type="button">Click me</button>
回答1:
It's not possible with your current implementation - every call of .bind
creates a new separate function, and you can only call removeEventListener
to remove a listener if the passed function is the same (===
) as the one passed to addEventListener
(just like .includes
for arrays, or .has
for Sets):
const fn = () => 'foo';
console.log(fn.bind(window) === fn.bind(window));
As a workaround, you could assign the bound function to a property of the instance:
class MyEl extends HTMLButtonElement {
constructor() {
super();
this.clickCount = 0;
this.boundListener = this.clickHandler.bind(this);
window.addEventListener('click', this.boundListener);
}
clickHandler(e) {
this.textContent = `clicked ${++this.clickCount} times`;
window.removeEventListener('click', this.boundListener);
}
}
customElements.define('my-el', MyEl, { extends: 'button' });
<button is="my-el" type="button">Click me</button>
回答2:
Create a wrapper func for your clickHandler like so.
class MyEl extends HTMLButtonElement {
constructor() {
super();
this.clickCount = 0;
this.wrapper = e => this.clickHandler.apply(this, e);
window.addEventListener('click', this.wrapper);
}
clickHandler(e) {
this.textContent = `clicked ${++this.clickCount} times`;
window.removeEventListener('click', this.wrapper);
}
}
customElements.define('my-el', MyEl, { extends: 'button' });
<button is="my-el" type="button">Click me</button>
回答3:
Another pattern is to keep your Listener inside the constructor.
To remove an Event Listener (no matter what pattern) you can add a 'remove' function the moment you create an Event Listener.
Since the remove function is called within the listen
scope, it uses the same name
and func
tion
pseudo code:
listen(name , func){
window.addEventListener(name, func);
return () => window.removeEventListener( name , func );
}
let remove = listen( 'click' , () => alert('BOO!') );
//cleanup:
remove();
Run Code Snippet below to see it being used with multiple buttons
Events bubbling UP & shadowDOM
to save you an hour once you do more with events...
Note that WebComponents (ie CustomElements with shadowDOM) need CustomEvents with the composed:true
property if you want them to bubble up past its shadowDOM boundary
new CustomEvent("check", {
bubbles: true,
//cancelable: false,
composed: true // required to break out of shadowDOM
});
Removing added Event Listeners
Note: this example does not run on Safari, as Apple refuses to implement extending elements : extends HTMLButtonElement
class MyEl extends HTMLButtonElement {
constructor() {
let ME = super();// super() retuns this scope; ME makes code easier to read
let count = 0;// you do not have to stick everything on the Element
ME.mute = ME.listen('click' , event => {
//this function is in constructor scope, so has access to ALL its contents
if(event.target === ME) //because ALL click events will fire!
ME.textContent = `clicked ${ME.id} ${++count} times`;
//if you only want to allow N clicks per button you call ME.mute() here
});
}
listen(name , func){
window.addEventListener( name , func );
console.log('added' , name , this.id );
return () => { // return a Function!
console.log( 'removeEventListener' , name , 'from' , this.id);
this.style.opacity=.5;
window.removeEventListener( name , func );
}
}
eol(){ // End of Life
this.parentNode.removeChild(this);
}
disconnectedCallback() {
console.log('disconnectedCallback');
this.mute();
}
}
customElements.define('my-el', MyEl, { extends: 'button' });
button{
width:12em;
}
<button id="One" is="my-el" type="button">Click me</button>
<button onclick="One.mute()">Mute</button>
<button onclick="One.eol()">Delete</button>
<br>
<button id="Two" is="my-el" type="button">Click me too</button>
<button onclick="Two.disconnectedCallback()">Mute</button>
<button onclick="Two.eol()">Delete</button>
Notes:
count
is not available asthis.count
but is available to all functions defined IN constructor scope. So it is (kinda) private, only the click function can update it.onclick=Two.disconnectedCallback()
just as example that function does NOT remove the element.
来源:https://stackoverflow.com/questions/54921027/remove-event-listener-that-has-been-added-using-bindthis