Gestion des raccourcis clavier ou raccourcis

Salut Nous parlerons des touches de raccourci dans WEBAPI + JavaScript, considérons leurs méthodes d'organisation et les problèmes qui surviennent principalement dans les grandes applications.


Envisagez des moyens de gérer les clés pour une tâche spécifique.


«Tâche»


Imaginez que dans un projet existant, vous devez implémenter un traitement de saisie au clavier. Dans le même temps, l'interface du projet et ses contrôleurs, pour des raisons purement historiques, bien sûr, telles qu'elles sont. Et il y a ce qui suit:


ParentController dans lequel il y a deux composants avec leurs états et leur état. Controller1 et un élément qui utilise CTRL+SHIFT+F pour rechercher le site, et Controller2 avec son élément DOM, qui est une zone locale, dans laquelle il y a une recherche à l'intérieur. Cependant, ils peuvent être simultanément à l'écran. Voici quelques façons de résoudre ce problème.


1. " KeyboardEvent et son traitement manuel"


Les objets KeyboardEvent décrivent comment l'utilisateur interagit avec le clavier. Chaque événement est décrit par une clé; le type d'événement (keydown, keypress ou keyup) détermine le type d'action produit.


Sonne bien non? Examinons de plus près.
Envisagez l'interception de la touche CTRL+SHIFT+F , correspondant généralement à un appel de recherche globale.


 element.addEventListener('keypress', (event) => { const keyName = event.key; //        // ..     SHIFT      if (event.ctrlKey && event.shiftKey && event.key.toLowerCase() === 't') { alert('CTRL+SHIFT+T pressed'); } }); 

Maintenant, applicable à notre tâche, vous pouvez faire deux façons (par exemple)


Effectuer l'interception des clés dans les contrôleurs 1 et 2 séparément


Cela entraînera le fait que, selon l'ordre dans le DOM, vous devrez peut-être useCapture pour garantir que le traitement de Controller2 puis Controller1 aura lieu. Vous obtenez donc une logique isolée, mais si l'application est complexe et qu'il y a beaucoup de tels contrôleurs, cette solution n'est pas bonne car certains peuvent être simultanément à l'écran et ils peuvent avoir leur propre ordre de traitement strict, qui ne dépend pas de leur position dans l'arborescence DOM. (voir bouillonnement et capture )


Capture de clé dans CommonController


Une solution alternative serait de gérer les clics dans un contrôleur parent commun, qui sait exactement quand afficher ses enfants, contrôlé par les premier et second contrôleurs. Cela, tout en augmentant les contrôleurs enfants, ne causera pas de difficultés avec la capture des événements et la décision du contrôleur pour lequel traiter les clés. Cependant, il y aura un autre problème - un épais if apparaît dans le contrôleur parent, qui gère tous les cas possibles. Pour les grandes applications, cette solution ne convient pas, car à un moment donné, un autre Controller peut apparaître qui n'est pas un enfant du ParentController alors vous devrez ParentController gestionnaire d'un niveau vers son parent commun et ainsi de suite ... Jusqu'à tôt ou tard l'un des contrôleurs commence à en savoir trop sur les éléments qu'il contient.



En fait, seulement 80% des navigateurs sont capables de travailler avec KeboardEvent.key , dans tout le reste, vous aurez besoin d'utiliser KeboardEvent.keyCode : Number avec des codes clés. Ce qui complique grandement la vie. Ici, il vaut la peine d'aller à une description des inconvénients de cette approche.


Inconvénients:


  • L'organisation du code n'est pas très pratique, elle nécessite une carte de codes de caractères et leur équivalent texte, et d'autres utilitaires qui réduisent la quantité de code dans les gestionnaires.
  • 80% Le soutien des navigateurs pour travailler avec des symboles sans utiliser leurs codes n'est pas encore suffisant.
  • Chevauchement à l'aide de useCapture un gestionnaire à un autre.
  • S'il existe des hooks avec useCapture et des éléments imbriqués avec les mêmes gestionnaires
    le débogage est difficile.
  • Mauvaise évolutivité.

Mais nativement, il n'y a pas de dépendances et bibliothèques inutiles


Ensuite, nous parlerons de deux bibliothèques, dont l'une a été conçue pour résoudre leurs propres problèmes similaires.


2. «Utilisation de la bibliothèque HotKeys »


Trois mille étoiles sur github, taille modeste et absence de dépendances. Un constructeur chinois, nous promet une solution qui conviendra à tout le monde. Mais ne nous précipitons pas. Essayons de résoudre notre problème avec son aide.


 //   hotkeys('ctrl+shift+f', function(event, handler){ alert('CTRL+SHIFT+T pressed'); }); 

La syntaxe semble déjà beaucoup plus courte et la puce principale pour résoudre le problème affichera directement les composants des contrôleurs 1 et 2 à l'écran. Après avoir fouillé un peu dans le code de la bibliothèque, il est facile de remarquer que les gestionnaires forment une pile qui est remplie ou effacée lorsqu'ils sont enregistrés aka apparaissant à l'écran (disons qu'un élément avec un gestionnaire qui est apparu plus tard que celui existant aura la priorité dans la file d'attente pour le traitement des touches de raccourci).


Il arrive souvent que l'élément qui doit intercepter le traitement apparaisse plus tard. Dans ce cas, nous pouvons diffuser en toute sécurité la logique de la gestion des clics à chacun des contrôleurs. Et d'autres puces telles que des étendues nous aideront à séparer un flux de clics d'un autre. Mais dans le cas où l' - les mêmes problèmes se posent que dans eventListener natif. Nous devrons tout mettre dans un contrôleur parent commun.


De plus, il arrive souvent que vous deviez bloquer le comportement par défaut, mais l'événement n'est pas considéré comme traité (en d'autres termes, il n'y a pas de compréhension sans équivoque si l'événement est traité ou non si nous le recevons) ou il doit être traité par deux contrôleurs simultanément. L'un d'eux provoquera une réaction au comportement, et l'autre tiendra simplement compte du fait que l'événement s'est produit.


Total des avantages:


  • La portée vous permet de séparer les flux.
  • La syntaxe est claire et courte.
  • L'ordre détermine l'apparence de l'élément, pas la position dans le DOM.
  • Taille et manque de dépendances.

Inconvénients:


  • Une seule étendue peut être traitée à la fois
  • Le débogage est toujours difficile à cause des appels de fonction dans la boucle, on ne sait peut-être pas sur quel gestionnaire perdu événement géré
  • La déclaration selon laquelle l'événement est traité s'il a l'indicateur defaultPrevented et sa distribution est interrompue n'est pas vraie.
  • Fonctions globales d'enregistrement des appels et de désabonnement aux événements

Il convient pour résoudre des tâches typiques, mais il y aura des problèmes avec l'écriture d'un terminal de trading ou d'un grand panneau d'administration, pour un débogage sûr.


3. «Utilisation de la bibliothèque de raccourcis de pile »


À la suite de nombreux râteaux et tentatives d'utilisation de la solution de quelqu'un d'autre, j'ai dû le vélo une bibliothèque qui aidera, tout d'abord, à faire ses débuts normalement, à conserver toutes les meilleures propriétés des plus populaires et à apporter quelque chose de nouveau.


Quelles tâches ont été résolues à la création?


  • Principe de fonctionnement réactif
  • Gestionnaires de débogage simples
  • Statut de traitement des événements sans ambiguïté
  • Multiplateforme
  • Commodité d'importation et manque de fonctions globales
  • Pas d'accès direct aux fenêtres lors de la connexion
  • Pas besoin d'appeler preventDefault ou stopPropagation

 //  this.shortcuts = shortcuts({ 'CMD+SHIFT+F': function (event, next) { alert('CMD+SHIFT+F pressed'); } }); //  this.shortcuts.destroy(); 

Applicable à notre tâche, la solution coïncide complètement avec la bibliothèque précédente. Une séparation complète de la logique de traitement sans trop de connaissances les uns des autres ne s'est toujours pas produite, mais beaucoup est devenu plus simple et plus compréhensible. Merci aux suivants:


  • Il n'y a toujours pas de liaison au DOM (à l'exception d'un écouteur) et la pile de gestionnaires est remplie en fonction de l'ordre dans lequel ils sont enregistrés.
  • scope immédiatement refusé d'utiliser l'isolement. il n'est pas clair quelles tâches il résout et il semble que cela ne fait que compliquer l'architecture.
  • Le débogage et la fonction suivante à ce sujet valent probablement plus ...
  • Mutations dans les événements des données qu'il porte dans event.detail

Les gestionnaires de débogage sont organisés de telle manière qu'une callstack est formée à partir d'eux avant l'appel. Il vous permet de voir dans la console toute la chaîne de l'événement du premier gestionnaire au suivant.


next () - Un appel de fonction signifie que l'événement n'a pas été traité et sera transmis au gestionnaire suivant. Un contrat assez familier qui s'applique aux gestionnaires intermédiaires ou middleware en express . Ainsi, vous saurez toujours si l'événement est traité ou simplement muté ou "pris en compte".



Voici à quoi ressemble la pile d'appels si vous définissez un point d'arrêt dans l'un d'eux.


Eh bien, sur les inconvénients:


  • Il n'y a pas encore de scripts de frappe.
  • Aucune portée - un site d'écran partagé ne peut pas être créé)
  • Une combinaison lors de l'inscription (ce n'est pas CMD+F,CMD+V,T ne comprendra pas la virgule)

Source: https://habr.com/ru/post/fr438770/


All Articles