Alors bienvenue dans ce nouveau chapitre où je vous propose de parler de la manière d'émettre nous-mêmes des événements en javascript. Donc on aimerait bien par exemple sur notre système de to-do list pouvoir détecter quand une tâche est supprimée. Donc on pourrait essayer de mettre de la logique compliquée dans notre code, mais vous allez le voir, le DOM nous permet déjà de faire des choses qui sont plutôt intéressantes, avec notamment la possibilité d'émettre nous-mêmes nos propres événements.
En effet, on l'a vu sur les champs ou sur à peu près n'importe quel type d'éléments, on a des événements qui sont natifs. Mais on aura la possibilité aussi de créer nous-mêmes nos propres événements. Alors pour cela, on va se baser dans notre fichier to-do-list.js et on va s'imaginer que lorsque l'on supprime un élément, on veut émettre un événement. Donc dans ce cas-là, on va devoir toujours partir d'un élément particulier. Donc nous, on va partir de notre li.
Et on va dire sur le li, j'aimerais bien envoyer un événement. Donc à ce niveau-là, on va pouvoir faire un vis.element pour récupérer notre élément. Et on pourra utiliser la méthode dispatchEvent.
Cette méthode va prendre un seul paramètre qui sera un événement. Vous pouvez utiliser les événements natifs, mais vous pouvez aussi créer vous-même vos propres événements. Pour cela, on utilisera un nouvel objet qui sont les CustomEvents.
Ces objets sont construits à partir d'une chaîne de caractère qui sera le nom de l'événement. Donc on peut faire ici un New CustomEvent. On va lui passer un premier paramètre, l'événement Delete.
Voilà, maintenant si on souhaite s'abonner à cet événement-là, il faudra tout simplement faire un AddEventListener et écouter l'événement Delete. On s'imagine qu'ici, par exemple, on fait un vise.dièseElement et j'aimerais bien rajouter un Listener sur Delete, qui n'est pas un événement standard du DOM, mais ce n'est pas gênant, et je récupérerai ensuite l'événement. Et cet événement, pour l'instant, je vais faire un Console.log et voir de quoi il en retourne. Si maintenant je me rends sur ma page et que j'ouvre ma console, lorsque je supprime l'élément, on voit bien que j'ai mon CustomEvent ici. Ce customEvent va contenir différentes propriétés qui correspondent à un événement classique.
On aura notamment le courant de target par exemple. Ne faites pas attention si la console vous dit nul. Pour avoir essayé, si vous essayez de débuguer courant de target, ça vous donnera bien la cible. Le problème, c'est qu'au moment où notre console affiche les choses, l'élément est supprimé du DOM et du coup le courant de target nous le donne comme nul. Autre petit détail, le dispatchEvent est synchrone.
C'est-à-dire que lorsqu'on fait un dispatchEvent, Tous les listeners vont être exécutés de manière synchrone. Et c'est qu'une fois tous les listeners exécutés, on passera à la tâche suivante. Donc si on avait du code bloquant à ce niveau-là, on ne supprimerait pas l'élément tant que les listeners n'ont pas été déclenchés. C'est complètement différent des événements natifs qui sont eux exécutés de manière asynchrone. Donc lorsque vous faites un dispatch event, ça va être synchrone.
C'est un petit détail, mais ça peut avoir son importance de temps en temps. Ensuite, en plus du nom de l'événement, on a la possibilité de passer un second paramètre qui sera un objet d'option. Une première option super intéressante, c'est Detail, qui nous permet d'afficher plus d'informations sur cet événement-là.
Donc si on a besoin de passer des données, c'est grâce à cette propriété Detail. Imaginons, nous, par exemple, on initialise la tâche à faire. Donc on va le faire dès la construction. Voilà.
Et on se dit, ça serait intéressant à ce niveau-là, de passer dans le détail la tâche que l'on vient de supprimer. Si je fais ça, lorsque je vais supprimer une tâche, on va voir que dans notre événement, Si je remets l'événement directement, on aura dans la partie detail les informations sur la tâche. Donc, on récupérerait l'objet original todo.
Ensuite, on a une autre propriété qui est intéressante, qui est bubble, qui va permettre de préciser si notre événement a un système de propagation. Pour prendre un exemple concret, imaginons que j'écoute ce même événement sur carrément notre body. Je vais faire un document.body.addEventListener et je vais écouter le même événement. Delete. ça prendra une fonction et dans ce cas là je ferai un console.log j'afficherai body suivi de mon événement si j'essaie de supprimer un élément on voit que ce console.log n'est pas utilisé donc l'événement ne se propage pas et il ne se propage pas parce qu'il n'a pas de propriété bubbles qui est à trous si ici je rajoute une propriété bubbles et que je mets trou dans ce cas là automatiquement lorsque je supprime on voit qu'on a à la fois l'événement qui est appelé sur notre élément mais il est aussi appelé au niveau du body parce que ça remonte vers l'élément parent.
Il faut savoir que cette propriété n'existe pas que sur les custom events, c'est une propriété qui est directement disponible au niveau de events. Vous avez ce système de bubbling qui est présent pour tous les événements par défaut. Par exemple, lorsque vous faites un clic, c'est quelque chose qui bubble, qui monte.
Mais certains événements ne vont pas être propagés parce qu'ils ont le bubble à false. Si vous voulez vérifier si un événement est capable de propager ou non, vous pouvez regarder cette propriété-là. pour avoir plus d'informations. Je ne sais pas si par contre c'était indiqué dans la documentation.
A première vue, je n'ai pas eu l'impression. Mais je ne sais pas si vous pouvez voir en un clin d'œil quels événements se propagent et quels événements ne se propagent pas. Ensuite, on a une autre propriété qui est cancellable, qui permet de préciser est-ce que finalement cet événement peut être annulé ou pas.
Si vous mettez un cancellable à true, vous allez avoir des informations supplémentaires et vous allez surtout avoir la possibilité d'utiliser la méthode preventDefault. Par exemple, je vais lui dire dans mon événement, lorsque je récupère mon événement, j'ai envie d'utiliser la méthode preventDefault. L'avantage, c'est qu'après, on peut vérifier est-ce que l'événement a été annulé.
Par exemple, à ce niveau-là, je pourrais me dire que je vais sauvegarder l'événement en amont. Je vais l'appeler event Je vais ensuite le passer à mon dispatch event. Tous les listeners vont se déclencher dessus. Et certains listeners pourraient faire un prevent default Après, je peux lui dire est-ce que sur cet événement, on a le default prevented ?
Si on a le default prevented, ça veut dire que quelqu'un a fait un prevent default et du coup, je ne souhaite pas appliquer le comportement par défaut, qui serait la suppression de l'élément. Donc là, je pourrais faire un return pour empêcher la suite de mon script de s'exécuter. Si j'essaie de supprimer un élément, on voit qu'il n'y a plus aucune suppression, parce qu'ici j'ai mis un preventDefault. Si je retire ce preventDefault, la suppression refonctionne. Donc cancelable vous permet d'indiquer que l'événement peut être annulé grâce à un preventDefault.
Donc voilà pour les trois propriétés à connaître sur les événements. Detail pour envoyer des informations, Bubbles pour propager l'événement aux éléments parents, ou propager dans l'autre sens si on a des éléments en mode capture, et cancelable qui va permettre à l'utilisateur de faire un prevent default si ça permet d'annuler des comportements par défaut. Nous, dans notre cas, par exemple, on pourrait annuler la suppression si on détecte certains comportements. Maintenant, comment on peut les utiliser dans des cas réels ? Les événements vont être super intéressants lorsqu'on a besoin de faire des communications enfant-parent.
Typiquement, nous ici, on a envie de penser que notre composant qui représente un élément dans notre liste, c'est un composant qui est isolé. Du coup, pour qu'il communique avec le reste de notre application, on va lui faire émettre des messages. Ensuite, ça sera à notre classe ToDoListItem de voir si elle souhaite agir ou non sur ces événements-là.
Ça nous permet de vraiment concevoir quelque chose d'isolé. On va garder le comportement que l'on a fait. On va dire que, par exemple, lorsque l'on supprime une ToDoList, effectivement, on va émettre l'événement Delete. De la même manière, lorsque l'on toggle une checkbox, on pourrait lui dire que ce serait intéressant d'émettre un événement pour dire qu'il y a eu un changement.
Là, par exemple, on pourrait envoyer une méthode Toggle. On lui passerait toujours la todo, on lui demanderait de bubble, et ensuite, le cancelable, je vous avouerai qu'il ne serait pas très utile. Maintenant, notre composant est capable d'émettre des événements en fonction de différentes situations qui lui arrivent.
On peut le replier complètement, et on peut se baser sur notre élément parent pour pouvoir capturer ces événements-là. Au niveau de notre append, on ne va pas être obligé d'écouter directement sur l'élément, vu qu'on a admis que tout pouvait bubbler. On va dire, j'aimerais bien sur mon liste element, venir écouter quand est-ce qu'un élément a été supprimé donc écouter l'événement des lettres dans ce cas là je recevrai un événement et la seule chose qui m'intéresse sur l'événement c'est le detail donc le detail je vais appeler ça tout doux et ensuite je mets ma petite fonction fléché donc là vous voyez c'est un cas d'utilisation de la déstructuration et je vais faire un console point log tout doux pour voir la tâche en question si maintenant j'essaie de supprimer un élément j'obtiens bien cette tout doux là Ce qui pourrait être intéressant, ça serait du coup de mettre à jour en interne la liste des tâches.
Je pourrais lui dire, j'aimerais bien que tu mettes à jour en interne todo et que tu lui dises de faire un vis.todo et je veux que tu filtres en retirant les tâches et en ne gardant que les tâches qui sont différentes de la tâche sur laquelle on est. Si ensuite je fais un console.log de vis.diestodo, dans ce cas-là, on va voir que si je supprime une tâche, on a notre tableau qui diminue en taille. De la même manière, je pourrais lui dire lorsque l'on a une checkbox qui est cochée, donc lorsqu'on a un toggle, j'aimerais bien que tu modifies la todo en question.
Là, on pourrait modifier l'objet. Vu qu'on modifie une propriété de l'objet, on fait une mutation et on fera un todo.completed égal différent de todo.completed. On est en train de changer.
Si c'était vrai, ça devient false et si c'est false, ça devient vrai. Donc si je coche tout ça, on changerait les valeurs à l'intérieur de notre objet. Je vais peut-être mettre dans les deux cas une console.log de vis.todo pour suivre un petit peu les changements.
Mais si maintenant je coche cette case-là et cette case-là, je ne vois pas de console.log en console, parce qu'effectivement, en bas, j'ai créé l'événement, mais j'ai complètement oublié de le dispatcher. Donc on ne va pas aller loin si on ne dispatche rien. Je dispatche mon événement event.
Donc si je recoche certaines cases, on va voir que... Au fur et à mesure, mon tableau est mis à jour. Il ne change pas de taille. Par contre, si je déplie mes éléments, je vois que là, je vais avoir bien un completed atroo pour l'ensemble de mes propriétés. Ça me permet comme ça, directement au niveau du parent, de pouvoir suivre l'évolution de mes tâches et de pouvoir mettre à jour mon tableau pour pouvoir ensuite le réutiliser plus tard.
Voilà comment vous pouvez utiliser les événements. Dès que vous avez besoin de faire communiquer un élément enfant avec son parent, les événements du navigateur sont très utiles. Je ne voulais pas forcément préciser, mais vous pouvez aussi utiliser dans les événements grâce au custom event des noms qui sont déjà utilisés.
Moi, personnellement, j'aurais tendance à vous déconseiller de faire ça parce que le problème, c'est que quand on écoute un événement change, on s'attend à ce que ça soit fait, par exemple, sur un input. On s'attend à avoir différentes propriétés. Par exemple, si on écoute un keydown, on s'attend à avoir une propriété clé. Si vous utilisez un custom event, vous vous retrouvez avec un événement personnalisé qui a le même nom que notre événement, mais qui n'a pas forcément les mêmes propriétés, ce qui peut être un petit peu perturbant. Donc personnellement, j'aurais tendance à vous conseiller de mettre des noms qui sont différents des noms natifs.
Mais voilà pour cet exemple là. Donc j'espère que ce petit exemple vous aura permis de comprendre un petit peu comment ça peut fonctionner et pourquoi ça peut être utile d'émettre des custom events sur votre code. Et je vous donne rendez-vous dans le prochain chapitre.