11487 sujets

JavaScript, DOM et API Web HTML5

Bonjour à tous
Je suis en train de modifier certains de mes fichiers JavaScript pour les mettre "à la mode".
Je tombe sur le point suivant pour lequel j'aimerais vos recommandations.
Soit une définition de classe d'objet "à l'ancienne"

function Photo(data) {
	for(let property in data) this[property] = data[property];
	this.display = function(event) {
		event.stopPropagation();
		......
	}
	this.figure = document.createElement(...);
       ....
	this.figure.addEventListener('click', this.display.bind(this));
}

Comme addEventListener référence la méthode this.display, je mets l'initialisation de la propriété this.figure après la définition de la méthode.

Si je veux transformer ce code en définition de classe JavaScript, j'écris le code suivant

class Photo {
      constructor(data) {
	    for(let property in data) this[property] = data[property];
        }
      display(event) {
		event.stopPropagation();
		......
	}
	/* que dois-je faire pour ce code ?
           this.figure = document.createElement(...);
            ....
	   this.figure.addEventListener('click', this.display.bind(this));
       */
}

Si je le mets dans constructor il référence une méthode qui n'est pas encore définie.
J'ai cru comprendre que les méthodes (contrairement aux fonctions en dehors de la définition de classe) n'étaient pas "remontées" (hoisted).

ou bien peut-on définir la classe de la façon suivante, en mettant le constructeur à la fin ?

class Photo {
      display(event) {
		event.stopPropagation();
		......
	}
	constructor(data) {
	    for(let property in data) this[property] = data[property];
            this.figure = document.createElement(...);          
            ....
	   this.figure.addEventListener('click', this.display.bind(this));
       }
}

Comment dois-je traiter ce problème ?
Modifié par PapyJP (23 Jul 2022 - 17:57)
J'ai essayé la dernière forme, cela semble fonctionner de façon satisfaisante.
Je suis toujours intéressé par vos avis.
Bonsoir PapyJP

Ta question m'a intrigué et j'ai donc reproduit une situation similaire à la tienne dans un Codepen pour expérimenter : https://codepen.io/GuillaumeBauer/pen/NWYarRE.

Voilà le code source du JS :
class Test {
  constructor(txt) {
    this.createElem("Click me !")
    this.logText = txt
  }
  
  createElem(btnText) {
    const main = document.querySelector("main")
    const child = document.createElement("button")
    child.textContent = btnText
    
    // La ligne ci-dessous ne fonctionne pas car le contexte n'est pas conservé
    //child.addEventListener("click", this.callback)
    
    // Cette version fonctionne car une arrow function fait un bind implicite
    child.addEventListener("click", event => this.callback(event))
    
    main.appendChild(child)
  }
  
  callback(e) {
    console.log(this.logText)
    console.log(e)
  }
}

let test = new Test("Clicked !")


Cet exemple créé un élément bouton, lui attache un callback pour l'événement "click" qui log dans la console une propriété de l'instance de la classe. Le bouton est ensuite ajouté comme enfant d'un élément de la page HTML.

J'ai pu remarquer deux choses :
1. Il est possible d'appeler une méthode de classe avant sa définition, comme tu peux le voir. Je n'ai pas trouvé de documentation qui indiquait un ordre à respecter pour la déclaration du constructeur et des méthodes dans une classe JavaScript.
2. Le fait de référence directement une méthode dans addEventListener ne conserve effectivement pas le contexte. En revanche, on peut utiliser une arrow function, qui va elle-même appeler notre méthode pour garder le contexte.

Enfin dernière remarque : sur le plan sémantique, un constructeur de classe a normalement uniquement vocation à simplement initialiser l'état d'un objet. Dans ton cas, le constructeur créé un élément et même s'il n'y a pas le code complet, j'imagine qu'il l'insère aussi dans le DOM.
L'idéal serait donc de séparer ces fonctions en gardant la partie initialisation dans le constructeur et en mettant la génération de l'élément dans une méthode à part.

Il sera toujours possible d'instancier un objet Photo en une seule ligne :
const maPhoto = new Photo(data).insertInto(domElement)
Merci de ta réponse

Je commence juste ma prise de connaissance d'ES6.

J'ai commencé par me faire une règle pour les let et const vs var.
En gros je continue à utiliser var pour les variables globales, essentiellement parce que ça me permet de m'y retrouver au premier coup d’œil, même si j'ai bien compris que pour la plupart des variables globales const serait peut-être préférable et let dans certains cas.
Je reverrai ça plus tard.

La deuxième étape est de mettre les définitions de classes d'objets à la nouvelle norme.

D'un point de vue design et programmation, les classes telles que définies dans ES6 me semblent constituer un énorme progrès.
Je n'ai pas encore abordé les fonctions fléchées, une chose à la fois Smiley cligne mais ce que tu en dis me semble également alléchant !

Pour répondre à ta question, non, le nouvel élément n'est pas inséré dans le DOM lors de l'initialisation de l'objet. On l'insère plus tard dans un nœud par
parentNode.appendChild(myPhoto.figure


Il est sûr que j'aurais pu/dû le créer lors de l'insertion en écrivant

class Photo {
      constructor(data) {
	    for(let property in data) this[property] = data[property];
        }
      display(event) {
		event.stopPropagation();
		......
	}
	figure() {
           let fig = document.createElement(...);
            ....
	  fig.addEventListener('click', this.display.bind(this));
          return fig;
}

et en l'insérant par
parentNode.appendChild(myPhoto.figure()


Je pense que c'est mieux comme code, je vais faire ça dès que ce @#! Smiley grouik d'hébergeur aura corrigé le bug qui a mis tous les sites utilisant PHP 7.3 hors service depuis 24h !!! Smiley fache
Bonsoir,

Au sujet des classes avec ES2015 (et également ES2022), j'ai regardé il y a quelques temps une video youtube qui m'a semblait très interessante sur le sujet.

C'est le formateur Christophe Porteneuve qui fait un cycle (14 videos) sur le JS moderne, dont une sur les classes.

https://www.youtube.com/watch?v=iamjfPsNc2U

Ca peut vous intéresser.
Mon serveur étant toujours indisponible j’ai fait quelques test avec des fonctions fléchées. C’est effectivement une technique très intéressante qui va me permettre de simplifier beaucoup mon code.
Merci pour vos conseils.