Bubble.js une solution 1.6K à un problème commun

L'une des tâches les plus courantes du développement Web est la gestion des événements. Notre code JavaScript écoute habituellement les événements envoyés par les éléments DOM.. 

Voici comment nous obtenons des informations de la part de l’utilisateur: c’est-à-dire qu’il clique, tape, tape, interagit avec notre page et que nous devons le savoir dès que cela se produit. L'ajout d'auditeurs d'événement semble trivial mais pourrait être un processus difficile.

Dans cet article, nous verrons un problème de cas réel et sa solution 1.6K.

Le problème

Un de mes amis travaille en tant que développeur junior. En tant que tel, il n'a pas beaucoup d'expérience avec JavaScript en vanille; Cependant, il a dû commencer à utiliser des frameworks tels que AngularJS et Ember sans avoir la compréhension fondamentale de la relation DOM-à-JavaScript. Au cours de son mandat en tant que développeur junior, il a été chargé d'un petit projet: sites Web de campagne d'une page ne nécessitant presque pas de JavaScript. Il a fait face à un petit mais très intéressant problème qui m'a finalement conduit à écrire Bubble.js.

Imaginez que nous avons un popup. Un bien stylé

élément:

Voici le code que nous utilisons pour afficher un message:

var popup = document.querySelector ('. popup'); var showMessage = function (msg) popup.style.display = 'block'; popup.innerHTML = msg; … ShowMessage ('Chargement en cours. Veuillez patienter.');

Nous avons une autre fonction masquerMessage cela change la afficher propriété à aucun et cache le popup. L'approche peut fonctionner dans le cas le plus générique, mais présente encore quelques problèmes. 

Par exemple, disons que nous devons implémenter une logique supplémentaire si les problèmes se présentent un par un. Disons qu'il faut ajouter deux boutons au contenu de la popup - Oui et Non.

var content = 'Êtes-vous sûr?
'; content + = 'Yes'; content + = 'No'; showMessage (contenu);

Alors, comment saurons-nous que l'utilisateur clique dessus? Nous devons ajouter des écouteurs d'événements à chacun des liens.. 

Par exemple:

var addListeners = function (yesCB, noCB) popup.querySelector ('. popup - yes'). addEventListener ('click', yesCB); popup.querySelector ('. popup - no'). addEventListener ('click', noCB);  showMessage (contenu); addListeners (function () console.log ('le bouton Oui a été cliqué');, function () console.log ('le bouton Non a été cliqué'););

Le code ci-dessus fonctionne lors de la première exécution. Que se passe-t-il si nous avons besoin d'un nouveau bouton, ou pire encore, si nous avons besoin d'un type de bouton différent? C’est-à-dire que si nous continuions à utiliser  des éléments mais avec des noms de classe différents? Nous ne pouvons pas utiliser le même addListeners il est ennuyeux de créer une nouvelle méthode pour chaque variation de popup.

Voici où les problèmes deviennent visibles:

  • Nous devons ajouter les auditeurs encore et encore. En fait, nous devons le faire chaque fois que le code HTML dans le popup
    est changé.
  • Nous pourrions attacher des écouteurs d'événements uniquement si le contenu de la fenêtre contextuelle est mis à jour. Seulement après voir le message appel. Nous devons penser à cela tout le temps et synchroniser les deux processus.
  • Le code qui ajoute les écouteurs a une dépendance difficile - le apparaitre variable. Nous devons appeler son querySelector fonction au lieu de document.querySelector. Sinon, nous pourrions sélectionner un mauvais élément.
  • Une fois que nous avons changé la logique du message, nous devons changer les sélecteurs et probablement le addEventListener appels. Il n'est pas sec du tout.

Il doit y avoir un meilleur moyen de le faire.

Oui, il y a une meilleure approche. Et non, la solution est de ne pas utiliser de framework.

Avant de révéler la réponse, parlons un peu des événements de l’arborescence DOM..

Comprendre la gestion des événements

Les événements sont une partie essentielle du développement Web. Ils ajoutent une interactivité à nos applications et agissent comme un pont entre la logique métier et l'utilisateur. Chaque élément DOM peut envoyer des événements. Tout ce que nous avons à faire est de souscrire à ces événements et de traiter l'objet Event reçu..

Il y a un terme propagation d'événement qui se tient derrière événement bouillonnant et capture d'événements deux méthodes de gestion des événements dans DOM. Utilisons le balisage suivant et voyons la différence entre eux.

clique moi

Nous allons attacher Cliquez sur gestionnaires d'événements aux deux éléments. Cependant, comme ils sont imbriqués les uns dans les autres, ils recevront tous les deux Cliquez sur un événement.

document.querySelector ('. wrapper'). addEventListener ('clic', fonction (e) console.log ('. wrapper cliqué');); document.querySelector ('a'). addEventListener ('click', function (e) console.log ('a clicked'););

Une fois que nous avons appuyé sur le lien, nous voyons la sortie suivante dans la console:

un clické .wrapper cliqué

Ainsi, en effet, les deux éléments reçoivent le Cliquez sur un événement. Tout d'abord, le lien et ensuite le

. C'est l'effet bouillonnant. Du plus profond possible à ses parents. Il y a un moyen d'arrêter le bouillonnement. Chaque gestionnaire reçoit un objet d'événement qui a stopPropagation méthode:

document.querySelector ('a'). addEventListener ('click', fonction (e) e.stopPropagation (); console.log ('un clic'););

En utilisant stopPropagation fonction, nous indiquons que l'événement ne doit pas être envoyé aux parents.

Parfois, nous pouvons avoir besoin d'inverser l'ordre et faire en sorte que l'événement soit capturé par l'élément externe. Pour ce faire, nous devons utiliser un troisième paramètre dans addEventListener. Si on passe vrai comme valeur nous ferons eprise d'air. Par exemple:

document.querySelector ('. wrapper'). addEventListener ('cliquez', fonction (e) console.log ('. wrapper a cliqué');, true); document.querySelector ('a'). addEventListener ('click', function (e) console.log ('a clicked');, true);

C’est ainsi que notre navigateur traite les événements lorsque nous interagissons avec la page..

La solution

D'accord, alors pourquoi avons-nous consacré une partie de l'article à la bulle et à la capture? Nous les avons mentionnées parce que la bulle est la solution de nos problèmes avec le popup. Nous devrions paramétrer les auditeurs de l'événement non pas sur les liens, mais sur les

directement.

var content = 'Êtes-vous sûr?
'; content + = 'Yes'; content + = 'No'; var addListeners = function () popup.addEventListener ('click', function (e) var link = e.target;); showMessage (contenu); addListeners ();

En suivant cette approche, nous éliminons les problèmes énumérés au début..

  • Il n'y a qu'un seul écouteur d'événement et nous l'ajoutons une fois. Peu importe ce que nous mettons dans le popup, la capture des événements se produira dans leur parent.
  • Nous ne sommes pas liés au contenu supplémentaire. En d'autres termes, on s'en fiche quand le voir le message est appelé. Tant que le apparaitre variable est en vie, nous allons attraper les événements.
  • Parce qu'on appelle addListeners une fois, on utilise le apparaitre variable aussi une fois. Nous ne devons pas le garder ou le passer entre les méthodes.
  • Notre code est devenu flexible car nous avons choisi de ne pas nous soucier du HTML transmis à voir le message. Nous avons accès à l'ancre cliquée dans cette e.target pointe sur l'élément pressé.

Le code ci-dessus est meilleur que celui avec lequel nous avons commencé. Cependant, cela ne fonctionne toujours pas de la même manière. Comme nous l'avons dit, e.target pointe sur le clic étiquette. Donc, nous allons utiliser cela pour distinguer le Oui et Non boutons.

var addListeners = function (callbacks) popup.addEventListener ('click', function (e) var link = e.target; var buttonType = link.getAttribute ('class'); if (rappels [buttonType]) rappels [ buttonType] (e);); … AddListeners ('popup - yes': function () console.log ('Yes');, 'popup - no': function () console.log ('No');) ;

Nous sommes allés chercher la valeur du classe attribuer et l'utiliser comme une clé. Les différentes classes pointent vers différents rappels.

Cependant, ce n’est pas une bonne idée d’utiliser le classe attribut. Il est réservé à l'application de styles visuels à l'élément et sa valeur peut changer à tout moment. En tant que développeurs JavaScript, nous devrions utiliser Les données les attributs.

var content = 'Êtes-vous sûr?
'; content + = 'Yes'; content + = 'No';

Notre code devient un peu mieux aussi. Nous pouvons supprimer les guillemets utilisés dans addListeners une fonction:

addListeners (yes: function () console.log ('Yes');, no: function () console.log ('No'););

Le résultat pourrait être vu dans ce JSBin.

Bubble.js

J'ai appliqué la solution ci-dessus dans plusieurs projets, il était donc logique de l'envelopper dans une bibliothèque. Il s’appelle Bubble.js et est disponible dans GitHub. C'est un fichier 1.6K qui fait exactement ce que nous avons fait ci-dessus.

Transformons notre exemple de popup pour utiliser Bubble.js. La première chose que nous devons changer est le balisage utilisé:

var content = 'Êtes-vous sûr?
'; content + = 'Yes'; content + = 'No';

Au lieu de action de données nous devrions utiliser action-bulle de données.

Une fois que nous incluons bubble.min.js dans notre page, nous avons un global bulle fonction disponible. Il accepte un sélecteur d'élément DOM et renvoie l'API de la bibliothèque. le sur La méthode est celle qui ajoute les écouteurs:

bulle ('. popup') .on ('oui', fonction () console.log ('Oui');) .on ('non', fonction () console.log ('Non'); )

Il existe également une syntaxe alternative:

bulle ('. popup'). on (yes: function () console.log ('Yes');, no: function () console.log ('No'););

Par défaut, Bubble.js écoute Cliquez sur événements, mais il y a une option pour changer cela. Ajoutons un champ de saisie et écoutons son keyup un événement:

Le gestionnaire JavaScript reçoit toujours l'objet Event. Donc, dans ce cas, nous pouvons afficher le texte du champ:

bulle ('. popup'). on (… entrée: fonction (e) console.log ('Nouvelle valeur:' + e.target.value););

Parfois, nous devons attraper non pas un mais plusieurs événements envoyés par le même élément. action-bulle de données accepte plusieurs valeurs séparées par une virgule:

Trouvez la variante finale dans un JSBin ici.

Les replis

La solution fournie dans cet article repose entièrement sur l'événement bouillonnant. Dans certains cas e.target ne peut pas pointer sur l'élément dont nous avons besoin. 

Par exemple:

S'il vous plaît, choisir moi!

Si nous plaçons la souris sur "choisir" et effectuons un clic, l’élément qui distribue l’événement n’est pas le tag mais le envergure élément.

Résumé

Certes, la communication avec le DOM est une partie essentielle de notre développement d’applications, mais il est de pratique courante que nous utilisions des frameworks pour contourner cette communication.. 

Nous n'aimons pas ajouter des auditeurs encore et encore. Nous n'aimons pas le débogage d'étranges bogues de déclenchement d'événements doubles. La vérité est que si nous savons comment fonctionne le navigateur, nous pouvons éliminer ces problèmes.

Bubble.js n’est qu’un résultat de quelques heures de lecture et d’une heure de codage - c’est notre solution 1,6K à l’un des problèmes les plus courants.