Je définis un élément personnalisé
// Only in the base class constructor() { super(); for (let prop of Object.getOwnPropertyNames(this.constructor.prototype)) { if (prop.indexOf("on") === 0) { this.addEventListener(prop.substring(2).toLowerCase(), this.constructor.prototype[prop]); } } }
https://jsfiddle.net/tuxkjp3q/
Mais quand je clique dessus, rien ne se passe.
Pourquoi cela et comment puis-je attacher des gestionnaires d'événements à chaque instance de la classe sans remplacer le constructeur?
Lorsque j'inspecte l'objet DOM, il y a un gestionnaire onclick, mais il ne fonctionne pas. Ceci est déroutant
D'autres découvertes, lorsqu'elles sont omises du prototype, la définition d'un gestionnaire fonctionne
customElements.define("my-button", class extends HTMLButtonElement { onclick() { console.log("bar"); } constructor() { super(); this.onclick = () => console.log("bar"); } }, { extends: "button" })
Mais lorsque le prototype contient onclick
, le même gestionnaire ne fonctionne plus
customElements.define("my-button", class extends HTMLButtonElement { constructor() { super(); this.onclick = () => console.log("bar"); } }, { extends: "button" })
Je serais vraiment heureux si nous pouvions obtenir une explication de ce phénomène.
J'ai trouvé une solution, si quelqu'un est confronté au même problème. Il est probablement intéressant de noter que seuls les gestionnaires définis dans le prototype direct de la classe instanciée seront attachés, ce qui est bon pour les performances mais probablement une logique défectueuse. Quoi qu'il en soit, il pourrait être adapté à vos besoins.
customElements.define("my-button", class extends HTMLButtonElement { onclick() { console.log("bar"); } }, { extends: "button" })
Je suis toujours très intéressé à en apprendre la raison.
3 Réponses :
Le problème est que vous devez définir l'écouteur du clic ... pas la fonction onClick
<button is="custom-button"> Hi </button>
html
class CustomButton extends HTMLButtonElement { constructor() { super(); this.addEventListener('click', () => { console.log('clicked'); }); } } customElements.define('custom-button', CustomButton, { extends: "button" });
J'ai spécifiquement souligné que je cherchais une solution sans surcharger le constructeur. En définissant la fonction onclick
je devrais définir ce qui est essentiellement l'attribut d'événement onclick
. Ce qui fonctionne bien si je le déclare dans my-button
comme une balise HTML, pas via JavaScript.
Tout ce que vous avez vraiment fait est de créer une propriété onclick
pour votre élément personnalisé et de lui attribuer une fonction. Ce n'est pas parce qu'il est nommé onclick
que le système sait qu'il s'agit d'un événement click
parce que vous créez votre propre élément. Vous devrez créer un événement personnalisé pour votre élément personnalisé.
Merci pour votre temps! Comme vu dans l'édition 2, s'il est ajouté en tant que propriété, le système sait qu'il s'agit d'un gestionnaire d'événements.
Vous êtes très émoussé à attacher tous les gestionnaires qui commencent par le
Devinez combien il pourrait y en avoir:
document.body.append( ...Object.getOwnPropertyNames(window).filter(prop => prop.indexOf("on") === 0) .map((prop, idx, arr) => { let div = document.createElement("div"); div.innerHTML = `${arr.length-idx} ${prop}`; return div; }));
Vous n'êtes pas obligé d'utiliser addEventListener
;
L'utilisation du ( ancien ) gestionnaire this.onclick=
dans Components peut être plus flexible,
car l'utilisateur du composant peut facilement l'écraser.
Avec addEventListener
l' auteur du composant doit fournir une méthode removeListener
; sinon l'utilisateur ne peut pas supprimer un écouteur.
Informations sur les différents modèles d'événements JavaScript:
Comme indiqué dans les commentaires, il s'agit d'une utilisation inappropriée de mon exemple. Essayez de l'exécuter sur window.constructor.prototype
et voyez combien il y en a.
Pourquoi ne voulez-vous pas remplacer le constructeur?
@Supersharp c'est une décision de conception, ou en termes simples - c'est moche. Il y a un problème supplémentaire, disons que j'utilise
addEventListener
mais mes écouteurs sont en fait liés au prototype de la classe et utilisent des nomson*
, puis un jour, le standard change et implémente le comportement attendu dans cette question, alors vous auriez le même handler appelé deux fois, ce qui entraînera probablement des applications cassées.FYI: Les Built-Ins personnalisés (étendant autre chose que HTMLElement) ne fonctionneront jamais dans Safari; Apple refuse de mettre en œuvre cette partie de la norme. Remarque: l'édition n ° 3 autorise toujours les définitions
this.onclick
. La raison pour laquelle cela ne fonctionne pas comme prévu (aujourd'hui) est que les gestionnaires en ligne remontent à 25 ans.this.onclick
est unethis.onclick
ancienne (totalement valide et la plupart du temps préférée); NOTATION différente des gestionnairesaddEventListener
(méthode préférée selon les manuels écrits après 2010) Lorsque laclass
ES6 a été introduite, ils (je suppose) ne voulaient pas la faire fonctionner avec une (ancienne) notation.Note 2: # 3 va vous causer des problèmes de mémoire ... vous ajoutez des écouteurs partout, mais vous ne les supprimez pas. Garbage Collect peut attraper la plupart / certains, mais généralement si vous ajoutez un écouteur, vous êtes également responsable de la suppression de cet écouteur (dans le
disconnectedCallback
)@ Danny'365CSI'Engelman me semble que le safari sera le nouveau, c'est-à-dire si Apple ne rassemble pas leur merde. Pourriez-vous fournir des sources pour la note 2? Je crois que les auditeurs sont rassemblés avec l'élément.
Les blogs GC habituels. Par votre question, je peux vous dire ce que vous faites. Moi-même, je ne ferais pas votre ... plutôt brutal ... edit # 3 cependant, vous attachez une merde d'auditeurs, en plus de cliquer, il y a aussi
ondragstart
et beaucoup de ses compagnons ... JavaScript est parfois une bête étrange, don ' t le combattre.@ Danny'365CSI'Engelman vous avez probablement manqué quelque chose, j'utilise actuellement une version légèrement modifiée de cette approche et mes éléments n'ont que quelques écouteurs attachés - autant que j'ai défini dans la classe elle-même. Peut-être avez-vous manqué qu'il n'attachera un écouteur que si la classe a sa propre propriété commençant par "on". Ce n'est pas très utile une fois que vous commencez à étendre une classe et que vous vous demandez pourquoi les
on*
s parent ne fonctionnent pas, donc dans ma version modifiée, j'ai autorisé cela avec un simple ajustement mais je ne l'ai pas ajouté ici car c'est un peu plus difficile pour comprendre et nécessite un appel.Consultez le code que j'ai joint à votre question. Vous combattez la bête JavaScript avec des centaines d'auditeurs
ON
inutiles ... Eh bien, ce n'est pas un combat , vous êtes tellement occupé avec des auditeurs que vous avez déjà perdus ...@ Danny'365CSI'Engelman Oui, c'est exactement ce que je voulais dire, ces propriétés ne seront pas prises en compte car mon exemple fonctionnera sur une classe qui étend
HTMLElement
qui ne les aura pas comme propriétés propres . Découvrez-le ici . Évidemment, cela devrait être utilisé comme un mixin ou quelque chose comme ça.C'est votre code, c'est votre fête.
this.onclick = this.onClick;
réalise la même chose. Eh bien ... presque le même, à proprement parler c'est un autre gestionnaire (c'est un "vieux" JavaScript, pas un Event Listener ajouté paraddEventListener
). Mais le but est qu'un clic sur un bouton soit traité. Avec les composants, je préfère cette notation, car un utilisateur peut facilement l'écraser ... ce qui ne peut pas être fait avec un écouteur ajouté avecaddEventListener
moins que le fournisseur de composants n'ait inclus une méthoderemoveListener
.@ Danny'365CSI'Engelman oui, c'est essentiellement cette question. Pourquoi cela fonctionne-t-il si je l'attribue dans le constructeur mais pas si dans le prototype. J'ai juste choisi la meilleure solution pour la solution de contournement.