Comment détecter une séquence de frappes au clavier en JavaScript

Photo par Aleksandar Cvetanovic sur Unsplash

L'une des fonctionnalités les plus utilisées de JavaScript est sa capacité à réagir à divers événements pouvant survenir pendant que l'utilisateur interagit avec la page Web. En fait, c’était l’idée même du JavaScript lorsqu’il est apparu, pour rendre les pages Web dynamiques en leur ajoutant une certaine interactivité. Avec JavaScript, nous pouvons réagir lorsqu'un utilisateur clique sur une partie spécifique de la page, lorsqu'une touche est enfoncée, lorsque la souris est déplacée, lorsque la page est chargée, lorsqu'un élément est concentré, etc.

Pour la liste de tous les événements JavaScript disponibles, consultez cette page MDN.

Bien qu’aujourd’hui le JavaScript puisse être utilisé pour faire beaucoup plus, cette fonctionnalité de base permettant d’ajouter de l’interactivité à la page Web est encore largement utilisée pour offrir aux utilisateurs des expériences riches et intéressantes.

Dans cet article, nous verrons comment utiliser JavaScript pour réagir aux événements de clavier, en particulier comment réagir à la séquence de touches spécifique saisie par l'utilisateur. Ainsi, lorsque l'utilisateur appuie sur une combinaison de touches, la page Web peut afficher du contenu, tel que l'ouverture d'un menu ou d'un modal, elle peut modifier le style de la page ou effectuer toute autre action que vous pouvez imaginer et qui figure dans les capacités JavaScript.

Avant de commencer, je voudrais simplement signaler que j’utilise certaines fonctionnalités de ES6 telles que const et let, l’opérateur de diffusion (...), les fonctions de flèche, etc. Si vous n’êtes pas familier avec celles-ci, consultez les liens fournis pour apprendre. plus.

Que construisons-nous?

Pour cet article, j'ai décidé de faire en sorte qu'un élément de la page modifie son image d'arrière-plan en fonction de la séquence de touches saisie par l'utilisateur. Donc, nous traitons essentiellement avec le style, rien de trop sophistiqué. Nous allons également ajouter des mises à jour de texte afin d'enrichir la démonstration et de fournir plus d'informations à l'utilisateur.

Mon idée était d'utiliser les séquences de touches utilisées comme codes de triche dans un vieux jeu FPS, Doom and Doom 2. En gros, lorsque l'utilisateur tape une combinaison de touches, le jeu offre certains avantages à l'utilisateur, tels que plus de munitions, plus de santé. , invulnérabilité, etc.

Dans l'exemple que nous allons construire, nous allons modifier l'image d'arrière-plan d'un élément de la page en fonction de la séquence de touches saisie par l'utilisateur. Vous pouvez voir et essayer la démo ici.

Projet de base

Afin de me concentrer uniquement sur JavaScript, j'ai créé du code HTML et CSS complet pour le projet et lié le fichier JavaScript contenant uniquement un journal de console pour garantir son fonctionnement. Vous pouvez télécharger les fichiers de projet initiaux ici ou vous pouvez créer les vôtres si vous préférez les faire à votre guise.

Voici une ventilation rapide du contenu JavaScript que nous avons initialement.

C’est assez simple, nous ajoutons un écouteur d’événements au document qui attend que le DOM soit complètement chargé, puis nous appelons la fonction de rappel. La fonction de rappel ne contient actuellement que l'instruction 'use strict' et un journal de console pour vérifier que le fichier est correctement lié à la page HTML.

Nous pouvons omettre cet écouteur d'événement et le rappel qui lui est transmis, mais le fichier de script doit ensuite être placé à la fin de l'élément HTML body pour s'assurer que le DOM est entièrement chargé lorsque le code javascript commence à s'exécuter. Si vous le souhaitez, n'hésitez pas à le faire. C’est justement la méthode que je préfère utiliser, avec l’avantage qu’il intègre également tout le code JavaScript dans une fonction et évite la pollution globale.

Ecouter les événements clés

L’étape suivante consiste à détecter le moment où l’utilisateur appuie sur une touche. Nous procédons de la même manière que nous avons ajouté l'écouteur pour l'événement DOMContentLoaded, mais nous allons maintenant spécifier un nom d'événement différent. Nous pouvons spécifier trois événements de clavier: keydown, keyup et keyypress.

  • keydown - se déclenche immédiatement lorsque la touche est enfoncée
  • keyup - se déclenche lorsque la touche est relâchée
  • touche enfoncée - se déclenche continuellement tant que la touche est maintenue enfoncée

A partir de cette description, nous pouvons immédiatement voir que la pression de touche n’est définitivement pas adaptée à notre objectif. Cela nous laisse avec keydown et keyup. Dans notre cas, les deux vont bien, mais dans une situation différente, l'une d'entre elles pourrait être plus appropriée que l'autre, vous devrez donc évaluer la situation et choisir en conséquence.

La seule différence que nous pouvons remarquer ici est que si nous utilisons keyup, cela peut sembler un peu lent, alors que keydown sent vraiment que la réaction est immédiate. Je choisirai keydown, mais comme il est possible d'atteindre le même objectif avec keyup, n'hésitez pas à utiliser l'événement que vous préférez.

Ajoutons maintenant l'écouteur d'événements et supprimons le journal de la console:

Il y a deux choses importantes à noter ici. Tout d'abord, nous avons fourni le nom de l'événement keydown à addEventListener, puis nous fournissons le paramètre event à la fonction de rappel du gestionnaire d'événements. Nous aurons besoin de ce paramètre d’événement pour vérifier quelle touche est enfoncée.

Donc, pour obtenir la clé qui a été pressée, nous pouvons accéder à la propriété key de l'objet event:

Astuce: pour voir quelles propriétés de l'objet événement sont disponibles, il suffit de faire console.log (événement) et de regarder l'objet enregistré. Je sais que cela peut sembler évident, mais si vous êtes débutant, vous pouvez essayer de chercher en ligne les options disponibles, ce qui est bien, mais la réponse est généralement simplement de vous déconnecter de la console.

Après avoir récupéré la clé event.key, nous la transformons en minuscule car il serait bien de rendre ces raccourcis clavier insensibles à la casse. Ce ne sera pas le cas à chaque fois, mais cette fois, ça ira parfaitement.

Si vous ouvrez la console du navigateur, cliquez à nouveau sur la page et essayez de taper quelque chose, vous pouvez voir que la clé sur laquelle vous avez appuyé est enregistrée dans la console. Mais si vous êtes attentif, vous remarquerez qu'il enregistre TOUTE touche enfoncée, ce qui signifie que des touches telles que Entrer, Supprimer, Retour arrière, etc. sont également enregistrées. C'est formidable et peut être utile, mais dans ce cas, je souhaite utiliser uniquement des lettres et des chiffres.

Par conséquent, ajoutons une vérification si la touche sur laquelle vous appuyez est une lettre ou un chiffre.

En créant la variable charList contenant tous les caractères qui nous intéressent, nous sommes en mesure de vérifier si la clé est présente dans la chaîne charList. Sinon, nous retournons simplement à partir de la fonction sans enregistrer le caractère.

Maintenant que nous écoutons les événements clés et que nous ne pouvons filtrer que les clés qui nous intéressent, il est temps de voir comment enregistrer une séquence de clés entrées.

Sauvegarde de la séquence de touches entrée

Pour enregistrer la séquence de clés entrées, nous devons ajouter une variable qui stockera cette séquence et que nous pouvons mettre à jour pour chaque événement de clé qui nous intéresse.

Nous pourrions utiliser une chaîne sur laquelle nous pouvons concaténer des clés, mais nous pouvons également utiliser un tableau dans lequel nous pouvons stocker des clés individuelles comme éléments de tableau. Dans ce cas d'utilisation simple, peu importe ce que nous utilisons, mais je vais utiliser l'approche par tableaux.

L'approche array est une approche suggérée par rapport à la concaténation de chaînes dans les cas où de nombreuses chaînes doivent être concaténées. Elle nous permet également d'utiliser toutes les méthodes array si nous en avons besoin.

Créons une variable appelée tampon, un tableau vide au début, et y introduisons les nouvelles clés.

Si nous définissons le tableau de mémoire tampon dans l'écouteur d'événements Keydown, il sera réinitialisé chaque fois qu'une touche est enfoncée et la séquence de clés entrée sera perdue. Pour cette raison, nous devons le définir en dehors de l'écouteur d'événements Keydown. Nous pouvons maintenant sauvegarder toutes les clés entrées.

Mais si nous regardons le journal, nous pouvons remarquer que nous enregistrons maintenant les clés tout le temps et que le tableau s'agrandit simplement avec de nouvelles touches enfoncées. Vérifiez le gif ci-dessous pour voir le résultat.

Nous ajoutons constamment des frappes dans le tableau de tampons

Voyez comment, après avoir écrit «bonjour» et un certain retard, lorsque je continue à taper, il conserve toutes les valeurs précédentes du tableau. Ce n'est pas très utile pour ce que nous voulons. Il serait bon de réinitialiser la liste des clés saisies après un certain temps écoulé depuis que l'utilisateur a entré la dernière clé.

Pour ce faire, nous pouvons utiliser l'intervalle de temps entre les deux dernières pressions sur une touche. Si cet intervalle est plus long que la durée spécifiée, nous réinitialiserons le tableau de mémoire tampon. Faisons cela ensuite.

Limiter l'intervalle de temps entre les appuis sur les touches

Pour cela, nous devons comparer le moment où la dernière frappe et la frappe actuelle se sont produites, puis vérifier si le délai entre celles-ci était supérieur à un délai souhaité, par exemple 1 seconde, 0,5 seconde, à votre convenance.

Nous avons donc besoin d’au moins une nouvelle variable pour enregistrer le dernier temps de frappe. Afin de pouvoir vérifier cette valeur, nous devons définir la variable en dehors de l'écouteur d'événements, puis la mettre à jour après une frappe au clavier.

Nous avons introduit deux nouvelles variables, lastKeyTime et currentTime. Nous pouvons le faire avec lastKeyTime seulement, mais il est beaucoup plus facile de raisonner avec la variable currentTime présente. Voyons ce que nous avons fait ici.

Tout d'abord, nous avons défini la variable lastKeyTime et l'avons initialisée avec une valeur d'heure actuelle. Nous avons dû l'initialiser car nous avons besoin de valeur pour effectuer un calcul ci-dessous. Nous pouvons utiliser zéro comme valeur ici, mais pour le rendre cohérent, utilisons la valeur temporelle actuelle.

Ensuite, nous avons défini la variable currentTime et l'avons également initialisée avec l'heure actuelle. À ce stade, quelqu'un pourrait demander pourquoi deux variables ayant la même valeur?! Gardez à l'esprit que ce ne sont que des valeurs d'initialisation, du moins pour lastKeyTime. Nous devrons mettre à jour cette valeur après chaque frappe. Et notez que lastKeyTime est initialisé en dehors de l'écouteur d'événements, c'est-à-dire une seule fois, alors que currentTime sera réinitialisé à chaque frappe.

Maintenant, nous avons une partie importante, la vérification si suffisamment de temps s'est écoulé entre les frappes. Si c'est le cas, nous voulons réinitialiser la variable buffer, le tableau contenant les caractères sur lesquels l'utilisateur a appuyé.

Pour ce faire, il suffit de soustraire lastKeyTime de la variable currentTime et de vérifier si le résultat est supérieur à un nombre. Dans l'exemple, ce nombre est 1000, c'est-à-dire 1000 millisecondes ou 1 seconde. C'est une bonne valeur de départ et il peut être modifié ultérieurement pour une meilleure mise au point.

Donc, si la condition currentTime - lastKeyTime> 1000 est vraie, nous réinitialisons le tampon dans un tableau vide: buffer = []; Après cela, le tampon recevra une nouvelle clé, mais ce sera la seule dans le tableau.

La dernière chose à faire est de mettre à jour la valeur lastKeyTime. Nous définissons simplement sa valeur comme étant la même que la valeur actuelle. Lors de la frappe suivante, lastKeyTime conservera l'heure à laquelle la dernière frappe a eu lieu, alors que currentTime obtiendra une nouvelle valeur.

Mise à jour de l'image de fond

Enfin, nous sommes prêts à faire ce que nous voulions, à changer l’arrière-plan en fonction de la séquence de clés que l’utilisateur a entrées.

Si l'utilisateur entre la séquence de touches correcte, nous devons saisir un élément sur la page et mettre à jour son arrière-plan en fonction des entrées de l'utilisateur. Il semble que nous ayons besoin de vérifier la séquence de touches correcte.

Bien que cela soit presque certainement vrai dans la plupart des cas, dans ce cas, ce n'est pas nécessaire. La nature de ce problème nous permet de sauter la vérification si nous sommes d'accord avec ce qui se passe si la séquence de touches n'est pas correcte.

Qu'est-ce que je veux dire par là? Laissez-moi vous expliquer en détail.

Si nous voulons changer l'image d'arrière-plan à l'aide de CSS, nous devons mettre à jour l'URL de l'image. Si les images portent le même nom que les séquences de touches, il suffit de lire l'entrée, d'en faire une chaîne et de définir celle-ci comme URL de l'image d'arrière-plan.

La chose intéressante est que si l'image n'existe pas, l'arrière-plan ne sera pas montré. Ce qui signifie que dans les cas où une image est affichée et que vous tapez quelque chose qui ne correspond pas à une URL d'image existante, l'image d'arrière-plan disparaîtra.

Dans ce cas, je suis d'accord avec cela, donc la vérification si l'URL est correcte n'est pas nécessaire. Mais si nous souhaitons conserver l'image actuellement affichée, cette vérification sera nécessaire. Faisons les choses simplement, c’est-à-dire sans cette vérification.

Nous avons supprimé console.log et ajouté une requête pour obtenir l'élément sur lequel l'arrière-plan sera affiché. À la ligne suivante, nous appliquons l'URL de l'arrière-plan.

Cette structure d'URL dépend de la structure de votre projet. Dans ce cas, les images se trouvent dans le dossier "images" et au format jpg. Il est important de noter ici que nous devons créer une chaîne en dehors du tableau de mémoire tampon afin qu’elle puisse être utilisée dans l’URL de l’image. Nous faisons cela en joignant simplement les éléments du tableau avec un caractère vide comme caractère de jointure, buffer.join ('').

Avec cela, nous avons atteint l'objectif principal de cet article. Maintenant, l'utilisateur peut essayer de saisir la séquence de touches correcte et si elle réussit, l'arrière-plan changera. Ce que nous avons fait est simple et fonctionnel et peut être facilement intégré à n’importe quel projet.

Mais il est encore possible d'apporter des améliorations. Donc, si vous aimez avoir plus de flexibilité dans votre code, continuez à lire.

Améliorer le projet

Le fait de regarder notre code dans son ensemble nous montre que nous utilisons certaines variables «globales». Global en ce sens qu'ils se trouvent dans le contexte supérieur de notre script, inclus dans la fonction d'écoute d'événement DOMContentLoaded où se trouvera tout le reste du code, ce qui signifie qu'ils se mélangeront à d'autres variables dans le contexte de niveau supérieur de notre script.

Mais la déclaration des variables buffer et lastKeyTime ne peut pas être insérée dans l'écouteur d'événements keydown, car elle les réinitialisera à chaque frappe et annulera la fonctionnalité.

Envelopper le code dans une fonction

Pour isoler le code qui contrôle nos événements clés, nous pouvons placer les variables et l'écouteur d'événements keydown dans une fonction distincte.

Avec cela, notre fonction keyMapper est complètement indépendante des autres codes de notre script et peut même être importée à partir d'un autre fichier pour rendre les choses encore plus claires. Mais maintenant que nous avons cette fonction, nous pouvons voir d’autres possibilités d’amélioration.

Supprimer les valeurs codées en dur

Une des choses essentielles que nous pouvons faire pour rendre la fonction plus flexible est de supprimer toutes les valeurs codées en dur que nous utilisons et de les transmettre à la fonction en tant que paramètres.

Les valeurs les plus importantes dont nous avons besoin et qui sont codées en dur sont le sélecteur de l’élément conteneur en arrière-plan (#background), les parties de l’url de l’image et la valeur de délai de cette vérification si (currentTime - lastKeyTime> 1000). Il y a aussi la variable charList, mais même si nous pouvons aussi la passer comme paramètre et contrôler exactement quels caractères sont autorisés, nous pouvons la laisser pour le moment pour simplifier les choses.

Au lieu de cela, nous pouvons utiliser la charList en la déplaçant hors de l'écouteur d'événements, dans la fonction keyMapper, pour éviter de la déclarer à chaque frappe. Alors, faisons tous ces changements.

Maintenant, la fonction keyMapper prend quatre paramètres qui nous permettent de la personnaliser plus facilement. Maintenant, quand une fonction a plus de trois paramètres, elle commence à se sentir un peu maladroite, il est facile de l’oublier ou de la mélanger. Nous réglerons cela un peu plus tard, mais pour le moment, allons-y.

Créer une fonction de rappel

L'action souhaitée de notre script consiste à mettre à jour l'arrière-plan d'un élément de la page. Cela se passe sur la dernière ligne du script. Mais vous souhaitez peut-être que quelque chose de complètement différent se produise après la saisie d'une séquence de touches spécifique par l'utilisateur, peut-être jouer un son, ouvrir une boîte de dialogue ou faire tout ce que vous pourrez penser.

Dans notre version actuelle du script, nous ne pouvons mettre à jour que l'arrière-plan. Mais ce serait vraiment bien si nous pouvions facilement dire à notre script si nous voulions faire autre chose, à savoir utiliser la fonction keyMapper pour pouvoir exécuter une fonction spécifique que nous avons définie. Pour ce faire, nous pouvons définir une fonction que nous pouvons transmettre à la fonction keyMapper, puis l'exécuter à partir de là.

Comme vous pouvez le constater, la nouvelle fonction keyMapper accepte un paramètre supplémentaire (dooh!), Callback, fonction que nous souhaitons exécuter après avoir appuyé sur une touche. Sur la dernière ligne de la fonction keyMapper, nous appelons cette fonction de rappel et lui transmettons certaines variables dont elle a besoin. Lorsque keyMapper est appelé, nous lui avons transmis la fonction nommée updateBackground qui est déclarée après la fonction keyMapper.

La fonction updateBackground contient essentiellement les deux dernières lignes de keyMapper que nous avons supprimées. Avec cela, nous avons complètement séparé l'enregistrement de la séquence de touches et l'action effectuée après l'obtention de cette séquence.

Si nous devions écrire une nouvelle fonction et la transmettre à keyMapper en tant que rappel au lieu de la fonction updateBackground, nous pourrions obtenir un comportement complètement différent.

Un pas en arrière

Une chose qui a commencé à me déranger beaucoup est le fait que nous avons maintenant cinq paramètres pour la fonction keyMapper et quatre pour la fonction updateBackground, et tous les paramètres pour updateBackground sont également transmis à la fonction keyMapper avant d'être transmis à la fonction keyMapper. fonction updateBackground.

Maintenant que nous avons séparé la fonction de rappel de la fonction keyMapper, il est logique de simplement coder en dur les valeurs requises dans la fonction updateBackground, car nous allons donc contenir toutes les informations requises dans la fonction de rappel et nous pouvons l'appeler avec un seul paramètre. , la séquence de touches fournie par keyMapper.

Cela signifie également que la fonction keyMapper peut être appelée avec seulement deux paramètres, keystrokeDelay et callback. Voici le script mis à jour:

Maintenant, cela semble un peu plus propre et la fonctionnalité et les paramètres sont bien séparés.

Néanmoins, nous pouvons faire un peu mieux que cela.

Vous avez peut-être remarqué qu’il existe une autre chaîne codée en dur dans la fonction keyMapper, le nom de l’événement keydown. Si nous décidions plutôt de faire fonctionner keyMapper sur l'événement keyup, nous devions le modifier directement dans la fonction. Ce n’est pas nécessairement un gros problème, mais ce serait bien si nous pouvions également contrôler les paramètres de ce creux.

Donc, nous ajoutons un troisième paramètre à la fonction keyMapper?

Nous pourrions le faire, mais allons un peu plus loin et n’en faisons qu’un paramètre. Introduisons…

L'objet options

Un moyen très pratique de fournir des paramètres pour des fonctions consiste à fournir à l'objet les propriétés qui contiennent les valeurs de paramètre. Cela est particulièrement utile dans les cas où de nombreux paramètres doivent être transmis à la fonction, en particulier dans les cas où ces paramètres sont facultatifs et représentent une sorte de configuration pour la fonction.

Les paramètres dont nous avons besoin pour la fonction keyMapper, keystrokeDelay et le nouveau type eventType à ajouter ressemblent vraiment aux valeurs de configuration. Un avantage supplémentaire de l'utilisation de l'objet pour transmettre les paramètres à la fonction est que l'ordre des paramètres n'a pas d'importance.

Voici comment nous pouvons le faire maintenant:

L'objet options contient maintenant le nom de l'événement à écouter et le délai entre les frappes. Dans la fonction keyMapper, nous avons également ces deux lignes:

const eventType = options && options.eventType || 'touche Bas';
const keystrokeDelay = options && options.keystrokeDelay || 1000;

C'est ici que nous avons rendu nos paramètres facultatifs. Si les valeurs sont fournies via l'objet options, elles seront utilisées. Sinon, nous utiliserons les valeurs par défaut. Vous pouvez tester cela en modifiant les valeurs dans l'objet options. Vous pouvez complètement omettre l’objet options dans l’appel à la fonction keyMapper et il fonctionnera toujours avec les valeurs par défaut.

Nous pouvons également transmettre la fonction de rappel via l'objet options, mais étant donné que, sans cette fonction, nous n'aurions aucune fonctionnalité utile, il semble préférable de le transmettre en tant que paramètre séparé et obligatoire.

Une autre chose à noter dans ce dernier changement est que l'ordre des paramètres pour la fonction keyMapper est inversé. Ce n'était pas nécessaire, mais si un paramètre est optionnel, il est généralement fourni après les obligatoires, sinon, si vous souhaitez omettre le paramètre d'options optionnelles, vous devrez appeler la fonction keyMapper comme ceci:

keyMapper (null, updateBackground); // null est la valeur de l'objet options

Ajouter la gestion d'état

Non, ce ne sera ni redux ni aucune autre bibliothèque de gestion d’état, il serait idiot d’ajouter toute la bibliothèque pour un script de cette taille. Nous allons simplement organiser nos variables qui contiennent l'état du script, le tampon et lastKeyTime, en un seul objet et le mettre à jour à chaque changement.

La séquence de touches et l'heure de la dernière frappe sont maintenant enregistrées en tant que propriété du tampon et propriété lastKeyTime de l'objet state. Sur chaque frappe, nous copions la propriété buffer dans la variable buffer locale et mettons à jour cette variable en conséquence.

Après cela, l'état est mis à jour avec la variable de tampon local et la variable currentTime. A la fin, le rappel est appelé avec la valeur actuelle du tampon.

Une chose à noter ici est que nous ne mettons jamais à jour directement l'objet state ni sa propriété de tampon, mais que nous créons toujours un nouvel objet pour l'état et un nouveau tableau pour le tampon. C'est ce qu'on appelle la mise à jour immuable de l'état et c'est le moyen privilégié de mettre à jour l'état. Nous ne changeons jamais la valeur / l'objet précédent, nous affectons toujours la nouvelle valeur / objet à l'état.

La ligne: tampon = [clé]; réinitialise la mémoire tampon dans le cas où il s'est écoulé plus de temps entre les frappes que le temps défini par keystrokeDelay.

La ligne tampon = [… state.buffer, key]; utilise l'opérateur spread ... pour remplir le nouveau tableau avec les valeurs du tampon d'état, puis nous ajoutons la clé actuelle au tableau. Avec cela, le tampon est mis à jour.

Enfin, la ligne state = {buffer: buffer, lastKeyTime: currentTime}; met à jour l'état en affectant le nouvel objet avec les nouvelles valeurs buffer et lastKeyTime.

Dans ce dernier exemple de code, j'ai supprimé le test de caractère car il n'améliore pas vraiment les fonctionnalités du script, mais vous pouvez bien sûr toujours ajouter votre propre contrôle pour contrôler le comportement du script.

Touche finale

Notre script est terminé maintenant. Nous pouvons définir des options, créer n'importe quelle fonction de rappel que nous voulons, l'état est mis à jour immuablement. Globalement, notre fonction keyMapper est maintenant assez flexible.

Si vous regardez de plus près la page dans le navigateur, vous remarquerez qu'il y a cette colonne à gauche avec certaines informations. Dans la partie inférieure de la colonne, vous pouvez voir les espaces réservés pour la séquence de touches ("certaines touches") que l'utilisateur a entrées et pour le message de retour ("rien pour le moment") pour l'utilisateur. Celles-ci sont actuellement inutiles, mais nous pouvons changer aussi facilement. Il y a plusieurs façons de le faire.

Le moyen le plus simple consiste à ajouter davantage de fonctionnalités à la fonction de rappel updateBackground. Mais cela changera alors ce que la fonction fait et son nom ne décrira pas correctement ce qu’elle fait.

Nous pouvons donc écrire notre nouveau code dans une autre fonction et appeler cette fonction à partir de la fonction updateBackground. Ce n’est pas une solution idéale, mais il vaut mieux gifler le nouveau code qui n’a rien à voir avec la mise à jour de l’arrière-plan dans la fonction updateBackground. Voici comment nous ferions cela:

Une autre façon de faire consiste à appeler simplement keyMapper une seconde fois avec la fonction updateUI en tant que fonction de rappel:

C'est un peu mieux en ce qui concerne la séparation des fonctionnalités, mais nous avons maintenant deux écouteurs d'événements qui font essentiellement la même chose.

Ce serait bien si nous ne pouvons avoir qu'un seul écouteur d'événements pour les événements clés, puis appeler toutes les fonctions souhaitées lorsque l'événement se produit.

Et nous pouvons le faire, nous n'avons besoin que de fournir toutes ces fonctions à la fonction keyMapper et la meilleure façon de le faire est de passer un tableau contenant les références à toutes les fonctions de rappel que nous voulons exécuter lorsque l'événement key se produit. Mettre à jour le code pour y parvenir est très simple:

Ainsi, la fonction keyMapper reçoit maintenant un tableau de références de fonctions:

keyMapper ([updateBackground, updateUI], options);

puis à chaque mise à jour du tampon, toutes ces fonctions sont appelées:

callbackList.forEach (callback => callback (tampon));

L'ajout de nouvelles fonctionnalités pour les événements clés est désormais aussi simple que d'écrire une fonction qui préforme ce que nous voulons et de transmettre sa référence à la fonction keyMapper lorsque nous l'appelons.

MISE À JOUR (21 février 2019)

Il a été mentionné dans les commentaires que l'utilisateur peut entrer un chemin tel que ../monimage pour accéder à un fichier d'un niveau supérieur à celui de la structure de répertoires… s'il existe. Pour éviter la possibilité d'utiliser des caractères autres que des lettres et des chiffres, ces lignes sont ajoutées à la fonction updateBackground:

const validKeys = keySequence.every (key =>! isNaN (parseInt (key)) || key.toLowerCase ()! == key.toUpperCase ());
if (! validKeys) retourne;

De plus, la fonction keyMapper renvoie désormais les clés non converties en minuscules. Cette conversion se produit maintenant dans les fonctions de rappel qui doivent le faire, comme updateUIfunction.

UPDATE (15 février 2019) - JavaScript dit que c'est vrai (0 == faux) et ceci ('' == faux)

Après avoir rappelé dans les commentaires sur les dangers de l’utilisation de l’évaluation des valeurs de vérité en JavaScript, j’ai décidé de remédier à ce problème et de corriger la partie problématique.

La question est dans ces lignes:

const eventType = options && options.eventType || 'touche Bas';
const keystrokeDelay = options && options.keystrokeDelay || 1000;

Comme je l'ai déjà mentionné, cela utilisera la valeur de l'objet options s'il existe, et s'il n'existe pas, la valeur par défaut sera utilisée. Sauf qu’il ne le fera pas dans tous les cas. Si pour une raison quelconque, options.eventType et options.keystrokeDelay ont des valeurs évaluées comme faussaires, ces valeurs ne seront pas utilisées et les valeurs par défaut seront ignorées.

Nous nous attendions à avoir la valeur indéfinie pour ces propriétés si elles n'existent pas, ce qui donnera une valeur false et nous utiliserons ensuite les valeurs par défaut. La valeur null peut aussi être évaluée à false, ce qui est également correct.

Ce que nous n’avons pas considéré ici, c’est le type des variables. En effet, en fonction du type de variable, d'autres valeurs de fausseté peuvent apparaître.

Nous nous attendons à ce que la première propriété, options.eventType, soit une chaîne. Mais que faire si c'est une chaîne vide? JavaScript indique qu'une chaîne vide doit être évaluée à false dans de telles comparaisons. Ce qui signifie que si nous passons une chaîne vide à la propriété options.eventType, nous aurons toujours l'événement keydown par défaut puisque la chaîne vide sera évaluée à false. Mais si on ne passe rien, ça ne marchera pas! Donc, dans ce cas, cela fonctionne réellement en notre faveur pour échouer cette vérification de la chaîne vide et utiliser le type d'événement keydown par défaut. C'est bon.

La deuxième propriété, options.keystrokeDelay, devrait être un nombre. Et dans le cas des nombres, JavaScript dit que le zéro doit être évalué à faux. Si nous voulions passer le zéro pour la valeur de délai, cela ne sera pas accepté et le retour à la valeur par défaut de 1000 ms. Mais par contre, je vous entends dire "Qui pourrait taper si vite que la valeur zéro devrait être prise en compte?". Et vous avez raison, dans ce cas tout cela est inutile, nous ne pouvions écouter qu'une touche à la fois.

Donc, nous sommes bien ici aussi, pas besoin de faire quoi que ce soit? En fait non. Et si le nombre fourni est négatif? Certes, il ne reviendra pas dans le passé et ne réagira pas aux pressions sur les touches avant qu’elles ne se produisent, mais cela se fera dès qu’elles se produisent. Fondamentalement, cela revient à définir la valeur à zéro mais cela passe le test de vérité. Nous voyons maintenant que nous avons besoin d'une autre condition ici, nous avons besoin de nombres supérieurs à zéro. Maintenant, nous avons résolu le problème, mais que se passe-t-il si quelqu'un passe un nombre comme 10? 10 ms suffisent-ils pour connecter deux frappes au clavier? Pas pour moi, c’est sûr. Je peux à peine le faire avec un délai de 250 ms. En tant que développeur de cette application, je ne vois aucune raison d'autoriser des retards inférieurs au minimum dont j'ai besoin pour connecter deux frappes au clavier. J'ai donc décidé de limiter le délai au minimum de 300 millisecondes.

Voici le code mis à jour pour la fonction keyMapper:

J'ai ajouté une fonction hasProperty qui vérifie si la propriété existe sur l'objet, et ce n'est qu'après cette vérification que nous pourrons passer aux vérifications suivantes. Sur la ligne du délai, vous pouvez également voir le contrôle options.keystrokeDelay> = 300 qui limitera le délai à 300 ms.

Une dernière chose que je voudrais mentionner est que les problèmes dont je parle dans cette section mise à jour peuvent tous être évités en premier lieu si nous écrivons des tests appropriés pour notre code. Par exemple, nous pouvons facilement penser aux problèmes liés à la chaîne vide lorsque nous examinons les entrées possibles de la fonction pour la propriété eventType. Il en va de même pour la propriété keystrokeDelay, tout en prenant en compte les entrées possibles, nous pouvons rapidement nous rendre compte que l'utilisation de zéro, de négatifs ou même d'un nombre trop petit ne fonctionne pas bien.

Conclusion

Même si la fonctionnalité de base de la réaction sur une séquence de touches spécifique était une tâche courte et simple, je voulais aller quelques pas plus loin et montrer comment nous pouvons réellement créer une fonction flexible pour nous permettre un peu plus de contrôle. Nous y sommes parvenus en passant la liste des fonctions de rappel et un objet options à la fonction keyMapper.

Mais vous pouvez rendre la fonction encore plus personnalisable en fournissant plus d'options et en ajoutant le code dans la fonction liée à ces nouvelles options.

En fonction de vos besoins, vous pouvez l’étendre autant que vous le souhaitez. Bien sûr, soyez raisonnable en faisant cela car vous voulez que votre code soit lisible et facile à raisonner.

J'espère que cet article vous a donné quelques idées sur la façon dont vous pouvez utiliser cela dans vos projets et n'hésitez pas à les partager dans les commentaires si vous le souhaitez.

Merci d'avoir lu et bonne année!