5
votes

Supprimer l'écouteur d'événements qui a été ajouté à l'aide de bind (this)

Comment puis-je supprimer l'écouteur de clics que j'ai lié à la fenêtre dans le constructeur ci-dessous? J'en ai besoin pour écouter sur window , et j'ai besoin d'accéder à l'instance de bouton à l'intérieur.

<button is="my-el" type="button">Click me</button>
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' });


2 commentaires

Contrôlez-vous le code JavaScript?


Est-ce que cela répond à votre question? Suppression de l'écouteur d'événements qui a été ajouté avec la liaison


3 Réponses :


7
votes

Ce n'est pas possible avec votre implémentation actuelle - chaque appel de .bind crée une nouvelle fonction séparée, et vous ne pouvez appeler removeEventListener que pour supprimer un écouteur si la fonction passée est le même ( === ) que celui passé à addEventListener (tout comme .includes pour les tableaux, ou .has pour les ensembles):

<button is="my-el" type="button">Click me</button>

Pour contourner ce problème, vous pouvez affecter la fonction liée à une propriété de l'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' });
const fn = () => 'foo';
console.log(fn.bind(window) === fn.bind(window));


0 commentaires

2
votes

Créez un wrapper func pour votre clickHandler comme ceci.

<button is="my-el" type="button">Click me</button>
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' });


1 commentaires

C'est celui qui a fonctionné pour moi, mais il faut noter que apply prend un tableau d'arguments et call les prend individuellement, donc j'ai fini par utiliser ce dernier pour passer sur l'événement.



1
votes

Un autre modèle consiste à conserver votre écouteur à l'intérieur du constructeur.

Pour supprimer un écouteur d'événement (quel que soit le modèle), vous pouvez ajouter un "remove" function au moment où vous créez un Event Listener.

Puisque la fonction de suppression est appelée dans la portée listen , elle utilise la fonction même nom et function

pseudo code:

<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> 

Exécutez l'extrait de code ci-dessous pour le voir utilisé avec plusieurs boutons

Les événements bouillonnent UP & shadowDOM

pour vous faire gagner une heure une fois que vous le faites plus avec des événements ...

Notez que les WebComponents (c'est-à-dire CustomElements avec shadowDOM) ont besoin de CustomEvents avec la propriété compose: true si vous veulent qu'ils passent vers le haut au-delà de sa limite shadowDOM

button{
  width:12em;
}

Suppression des écouteurs d'événements ajoutés

Remarque: cet exemple ne fonctionne pas sur Safari, car Apple refuse de mettre en œuvre l'extension d'élem ents: étend 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' });
    new CustomEvent("check", {
      bubbles: true,
      //cancelable: false,
      composed: true       // required to break out of shadowDOM
    });
  listen(name , func){
    window.addEventListener(name, func);
    return () => window.removeEventListener( name , func );
  }

  let remove = listen( 'click' , () => alert('BOO!') );

  //cleanup:
  remove();

Notes:

  • count n'est pas disponible en tant que this.count mais est disponible pour toutes les fonctions définies dans la portée du constructeur. Donc c'est (un peu) privé, seule la fonction clic peut le mettre à jour.

  • onclick = Two.disconnectedCallback () à titre d'exemple, cette fonction ne supprime PAS l'élément.


5 commentaires

Soit je ne comprends pas, soit vous avez manqué la partie cruciale .bind (this) lors de l'ajout de l'auditeur (qui est la cause réelle de mon problème).


bind () concerne la définition de la portée, puisque la fonction d'écoute de cet exemple s'exécute dans la portée constructor , il n'est pas nécessaire d'utiliser bind () . Je mettrai à jour la première phrase pour dire «un autre modèle»


.. et si votre gestionnaire de clics est énorme et que vous avez des milliers et des milliers de boutons et que la mémoire est un problème, vous pouvez bien sûr lier une fonction de classe. La même approche d'écoute / silence pour un EventListener s'applique ► ME.mute = Me.listen ('click', ME.handler.bind (ME));


Malheureusement, votre compréhension de ceci vous échoue ici. La fonction s'exécute dans la portée de l'élément / objet auquel l'écouteur est attaché, sauf si vous utilisez .bind (this) .


Désolé, je n'ai pas été assez clair. Voici deux modèles différents non utilisant bind () jsfiddle .net / dannye / a6nvk173