Le sélecteur de poids global (#fontWeight) ne déclenche aucune mise à jour après un changement de police, alors que les sélecteurs de poids par ligne (.line-font-weight) fonctionnent parfaitement.
Deux mécanismes d'event binding concurrents sur le même élément #fontWeight :
document.getElementById('fontWeight').addEventListener('change', (e) => {
this.fontWeight = e.target.value;
this.render();
});- Attaché une seule fois au démarrage
- Utilise
.addEventListener()
globalSelect.onchange = (e) => {
this.fontWeight = e.target.value;
this.render();
};- Attaché à chaque changement de police
- Utilise
.onchange =(propriété DOM)
Le mélange de .addEventListener() et .onchange = sur le même élément crée un conflit.
init()
├─ loadGoogleFonts()
│ └─ loadFontWeights('Montserrat')
│ └─ updateWeightSelectors()
│ └─ globalSelect.onchange = handler1 ← PREMIER
│
└─ bindEvents()
└─ addEventListener('change', handler2) ← DEUXIÈME
Résultat : Les deux handlers coexistent, ça fonctionne.
selectFont('Lato')
└─ loadFontWeights('Lato')
└─ updateWeightSelectors()
└─ globalSelect.innerHTML = newOptions
└─ globalSelect.onchange = handler3 ← REMPLACE handler1
Résultat :
handler2(addEventListener) toujours làhandler3(.onchange) remplacehandler1- Conflit entre les deux mécanismes → rien ne se passe
Dans renderLineEditors() (ligne 984-1001) :
// Élément CRÉÉ dynamiquement
const weightControl = document.createElement('div');
weightControl.innerHTML = `<select class="line-font-weight">...</select>`;
// Listener attaché UNE SEULE FOIS avec addEventListener
const weightSelect = weightControl.querySelector('.line-font-weight');
weightSelect.addEventListener('change', (e) => {
this.lines[lineIndex].fontWeight = e.target.value;
this.render();
});Différences clés :
| Aspect | Global #fontWeight |
Par ligne .line-font-weight |
|---|---|---|
| Élément | Statique (HTML) | Créé dynamiquement |
| Binding | .addEventListener() + .onchange = |
.addEventListener() seul |
| Rebinding | Oui (à chaque changement police) | Non (nouvel élément) |
| Conflit | OUI | Non |
Supprimer le .onchange = dans updateWeightSelectors() et ne garder que le listener de bindEvents().
Modification dans updateWeightSelectors() (lignes 377-384) :
// AVANT (problématique)
globalSelect.innerHTML = buildOptions(this.fontWeight);
globalSelect.onchange = (e) => {
this.fontWeight = e.target.value;
this.render();
};
// APRÈS (corrigé)
globalSelect.innerHTML = buildOptions(this.fontWeight);
// Pas de re-binding - le listener de bindEvents() suffitAvantage : Simple, cohérent avec les sélecteurs par ligne.
Supprimer le listener dans bindEvents() et ne garder que .onchange dans updateWeightSelectors().
Modification dans bindEvents() (lignes 624-627) :
// AVANT
document.getElementById('fontWeight').addEventListener('change', (e) => {
this.fontWeight = e.target.value;
this.render();
});
// APRÈS
// Supprimer complètement - géré par updateWeightSelectors()Inconvénient : Le premier changement avant updateWeightSelectors() ne fonctionnerait pas.
Utiliser une référence nommée pour pouvoir supprimer l'ancien listener.
// Dans la classe
this.weightChangeHandler = (e) => {
this.fontWeight = e.target.value;
this.render();
};
// Dans bindEvents()
document.getElementById('fontWeight').addEventListener('change', this.weightChangeHandler);
// Dans updateWeightSelectors()
globalSelect.removeEventListener('change', this.weightChangeHandler);
globalSelect.addEventListener('change', this.weightChangeHandler);Inconvénient : Plus complexe, nécessite de stocker la référence.
Le commentaire existant dans le code suggère une méfiance envers innerHTML :
// Re-bind the change event (innerHTML may affect it in some browsers)Si dans certains navigateurs innerHTML sur un <select> casse les listeners, supprimer simplement .onchange = ne suffira pas.
Principe : Utiliser uniquement .addEventListener() partout, mais avec une référence nommée pour pouvoir nettoyer.
this.opentypeFonts = {};
this.renderCounter = 0;
this.weightChangeHandler = null; // AJOUTER// AVANT
bindEvents() {
// Font weight
document.getElementById('fontWeight').addEventListener('change', (e) => {
this.fontWeight = e.target.value;
this.render();
});
// APRÈS
bindEvents() {
// Font weight - handler défini ici, sera aussi utilisé par updateWeightSelectors
this.weightChangeHandler = (e) => {
this.fontWeight = e.target.value;
this.render();
};
document.getElementById('fontWeight').addEventListener('change', this.weightChangeHandler);// AVANT
globalSelect.innerHTML = buildOptions(this.fontWeight);
// Re-bind the change event (innerHTML may affect it in some browsers)
globalSelect.onchange = (e) => {
this.fontWeight = e.target.value;
this.render();
};
// APRÈS
globalSelect.innerHTML = buildOptions(this.fontWeight);
// Re-bind avec addEventListener (cleanup + re-attach)
if (this.weightChangeHandler) {
globalSelect.removeEventListener('change', this.weightChangeHandler);
globalSelect.addEventListener('change', this.weightChangeHandler);
}- Unicité : Un seul handler, une seule méthode (addEventListener)
- Cleanup : On supprime l'ancien listener avant d'en ajouter un nouveau
- Cohérence : Même pattern que les sélecteurs par ligne
- Robustesse : Fonctionne même si innerHTML casse les listeners
Si on est certain que innerHTML ne casse pas les listeners addEventListener :
Simplement supprimer les lignes 379-383 :
// Re-bind the change event (innerHTML may affect it in some browsers)
globalSelect.onchange = (e) => {
this.fontWeight = e.target.value;
this.render();
};Le listener de bindEvents() restera attaché et fonctionnera.
Risque : Si innerHTML casse les listeners dans certains navigateurs, ça ne marchera pas.
| Fichier | Ligne | Action |
|---|---|---|
app.js |
~123 | Ajouter this.weightChangeHandler = null; |
app.js |
628-633 | Extraire le handler dans une propriété |
app.js |
379-383 | Remplacer .onchange = par removeEventListener + addEventListener |
| Fichier | Ligne | Action |
|---|---|---|
app.js |
379-383 | Supprimer le bloc .onchange = ... |