web-dev-qa-db-fra.com

Alternative pour la propriété KeyboardEvent.which obsolète de l'événement

MDN indique que KeyboardEvent.which est déconseillé. Comment puis-je le remplacer par une version non obsolète?

Par exemple, j'ai les éléments suivants:

window.onkeydown = (event) => { console.log(event.which); }

Je pensais que event.key.charCodeAt() pourrait remplacer event.which, Mais cela ne fonctionnera pas pour des clés telles que ALTCTRL ou ENTER, et cela ne fonctionne que si event.key.length === 1:

window.onkeydown = (event) => { console.log(event.key.charCodeAt()); }

Pour récapituler, event.which != event.code Et event.which != event.key, Je ne peux donc pas simplement utiliser event.key.

Existe-t-il un substitut à event.which Qui détecte les combinaisons de touches, y compris ALTCTRL ou ENTER?

10
flen

TL; DR: Ce sont les règles que vous devez suivre:

  • Lors de la saisie de texte de l'utilisateur, utilisez l'événement keypress avec e.key
  • Pour les raccourcis et autres combinaisons, la méthode intégrée consiste à utiliser keydown/keyup et à vérifier les différentes touches de modification. Si vous devez détecter des accords, vous devrez peut-être créer une machine à états.

Contexte

La saisie au clavier est divisée en deux phases - paires de touches enfoncées/enfoncées, qui suivent les touches physiques enfoncées, et les caractères composés qui combinent plusieurs séquences de touches pour calculer un caractère.

Obtenir du "texte"

Si vous voulez savoir ce que le système d'exploitation pense que la séquence composée est, vous devez utiliser KeyboardEvent.key

Exemple de code:

document.getElementById('a').addEventListener('keypress', e => console.log(e.key));
<input id="a" type="text" placeholder="type here">

La raison pour laquelle vous voulez le faire la plupart du temps est que de nombreuses langues composent des caractères avec plusieurs touches. Le plus simple à comprendre pour les claviers US-101 est d'appuyer sur la touche shift + a est A, par rapport à une simple pression sur a. Pour les langues comme le russe, avec la touche morte altgr , cela devient particulièrement important.

Le point que j'essaye de faire est que faire tout ce travail vous-même - détecter les séquences de touches et déterminer la sortie de texte correcte est un problème difficile ™. C'est le travail du système d'exploitation pour une raison.

Désormais, pour les navigateurs plus anciens, vous ne souhaiterez peut-être pas utiliser e.key Faute de support plus ancien. Ensuite, vous pouvez revenir à des choses comme which, ou à d'autres approches non standard.

À un moment donné dans le futur, la pression de touche peut être supprimée par les navigateurs. L'événement beforeinput est censé être le remplacement. Cependant, cet événement n'est pris en charge que dans Chrome, donc j'omet ici pour des raisons de brièveté.

Obtenir des frappes

Supposons maintenant que vous ne suivez pas le texte, mais plutôt les séquences de touches. C'est pour des choses comme des jeux, ou écouter ctrl-c Etc. Dans ce cas, la bonne chose à faire est d'écouter keydown/keyupévénements . Pour les touches de modification, vous pouvez simplement écouter les propriétés ctrlKey, shiftKey et metaKey de l'événement. Voir ci-dessous:

document.getElementById('a').addEventListener('keydown', (e) => {
  const states = {
    alt: e.altKey,
    ctrl: e.ctrlKey,
    meta: e.metaKey,
    shift: e.shiftKey,
  };
  const key = e.key;
  const code = e.code;
  console.log(`keydown key: ${key}, code: ${code}`, states);
});
<input id="a" type="text" placeholder="press ctrl">

Par exemple, lorsque j'appuie sur shift-o sur mon clavier, j'obtiens ce qui suit:

keydown key: Shift, code: ShiftLeft {
  "alt": false,
  "ctrl": false,
  "meta": false,
  "shift": true
}
keydown key: O, code: KeyS {
  "alt": false,
  "ctrl": false,
  "meta": false,
  "shift": true
}

Espérons que la partie states soit assez évidente. Ils disent si cette touche de modification a été enfoncée alors que l'autre touche est enfoncée.

La différence entre key et code a à voir avec les dispositions du clavier. J'utilise la mise en page du logiciel dvorak. Ainsi, lorsque je tape la touche s, le code de numérisation qui va au système d'exploitation dit s, mais ensuite le système d'exploitation le convertit en o parce que c'est dvorak. Dans ce cas, le code fait toujours référence au code de numérisation (touche physique enfoncée), tandis que la touche correspond au meilleur effort du système d'exploitation pour déterminer ce que sera le "texte". Ce n'est pas toujours possible, surtout avec d'autres langues. Encore une fois, c'est pourquoi utiliser la clé pour keypress est la bonne façon de procéder.

Bibliothèques tierces

Si cela ne semble pas particulièrement facile, c'est parce que ce n'est pas le cas. La dernière fois que j'ai regardé cela, je suis tombé sur la bibliothèque souricière , bien que je ne sois pas sûr de le recommander, étant donné certains des problèmes que j'ai trouvés. Il montre cependant un exemple de construction d'une machine à états pour suivre les accords clés.

Addenda

C'est aussi pourquoi vous devez suivre keydown/keyup si vous voulez manger des frappes. Puisqu'il n'y a pas de "texte" pour ctrl + c, vous n'obtiendrez pas une touche correcte, et donc le navigateur le gèrera nativement. Si vous souhaitez exécuter votre propre comportement, vous devez e.preventDefault() sur la touche elle-même. (Certains des événements de suivi comme copy peuvent également être annulés, mais ce n'est pas universellement vrai)

Si vous avez également besoin de suivre les clés insérées après coup dans un champ de saisie (ou div contentable), consultez l'événement input .

Historique: mis à jour le 8/2019 pour changer la pression de touche -> avant entrée

16
AnilRedshift

Comme les autres réponses l'ont souligné, _event.which_ a un problème principal: il ne renvoie pas le même numéro pour différents navigateurs ou ordinateurs (c'est peut-être la raison pour laquelle il est obsolète). Par conséquent, il n'y a pas de substitut parfait, car il produira des numéros différents pour différents utilisateurs.

Donc, le principal problème en essayant d'en créer un substitut (appelons-le: function whichSubstitute(event)) est que le Meta et Shift les touches, par exemple, n'ont pas de numéro unique que whichSubstitute devrait obtenir lorsque l'une d'elles est pressée, cela varie selon le système d'exploitation .

Dans cet esprit, il existe deux approches pour obtenir le point de code unicode pour l'entrée de l'utilisateur.

  1. Obtention de la valeur unicode du caractère que l'utilisateur a entré (par exemple, _ü_, qui serait 'ü'.codePointAt(0)).
  2. Obtention d'une valeur numérique pour le caractère qui correspond à la touche physique appuyée sur le clavier, qui peut être différente de celle entrée dans le champ de texte. Comme AnilRedShift l'a mentionné, la disposition du clavier peut modifier la sortie "naturelle" de cette touche du clavier, de telle sorte que la touche s peut afficher o. Dans ce cas, nous obtiendrions 's'.codePointAt(0), au lieu d'obtenir la valeur de _'o'_ (c'est-à-dire, ce qui a été réellement sorti), comme nous obtiendrions en utilisant la première approche. En savoir plus sur MDN :

Par exemple, le code renvoyé est "KeyQ" est pour la touche "q" sur un clavier de disposition QWERTY, mais la même valeur de code représente également le "_'_" sur les claviers Dvorak et la touche "a" sur les claviers AZERTY. Cela rend impossible l'utilisation de la valeur du code pour déterminer le nom de la clé pour les utilisateurs s'ils ne le sont pas en utilisant une disposition de clavier prévue.

En bref: l'approche numéro 1 obtient le point de code unicode pour _ü_, tandis que l'approche numéro 2 obtient les points de code pour __ SHIFT6 et U (puisque SHIFT+6+U == _ü_).

Dans cette réponse, nous utiliserons String.prototype.codePointAt() au lieu de String.prototype.charCodeAt(). Les différences sont bien expliquées ici . La raison en est que nous pouvons obtenir le numéro unicode entier avec .codePointAt(0), tandis que .charCodeAt(0) manquerait .codePointAt(1) pour terminer le point de code codé UTF-16.

Pour l'approche numéro 1 , nous pouvons utiliser le code suivant:

_function whichSubstitute(event) {
  const theKey = event.key;
  if (theKey.length === 1) {
    return theKey.codePointAt(0);
  }
  switch (theKey) {
    case "Backspace":
      return 8;
    case "Tab":
      return 9;
    case "Enter":
      return 13;
    case "Alt":
      return 18;
    case "Escape":
      return 27;
    case "Delete":
      return 127;

    case "Dead": //As of july 2018, Firefox has no support for "Dead" keys https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
      {}
      break;
    case "Unidentified":
      alert("handle the 'Unidentified' if you want to!");
  }

  /*** there are many other possible key values https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
        but, AFAIK, there are no unicode code points for them, such as:

  switch (theKey) {
    case "AltGraph":
    case "CapsLock":
    case "Control":
    case "Fn":
    case "FnLock":
    ...

       event.which may output some number for them, but it is not consistent across
       browsers/machines and they may overlap with other code points. For example:

  case "ArrowUp":
    return 38; //this overlaps with "&"
  case "ArrowLeft":
    return 37; //this overlaps with "%"
  case "ArrowDown":
    return 40; //this overlaps with "("
  case "ArrowRight":
    return 39; //this overlaps with "'"

  ***/

  return 0;
}

//test
document.onkeydown = (event) => {
  console.log('whichSubstitute: ' + whichSubstitute(event) + '; event.which: ' + event.which);
  //note that whichSubstitute gets the ASCII number of 'a', while event.which only gets the ASCII number of 'A' (upper case, always)
}_

Bien sûr, cela ne résout pas le problème d’obtenir uniquement un unique numéro cohérent pour une touche enfoncée quand il n’y a pas de point de code unicode pour cela (comme dans le cas de Meta). Ces clés doivent être manipulées par le programmeur en fonction de ses besoins.

Pour l'approche numéro 2 , nous pouvons utiliser le code suivant:

_function whichSubstitute(event) {
  const theChar = event.code;
  if (theChar.startsWith('Key')) {
    return theChar.codePointAt(3);
  }
  if (theChar.startsWith('Digit')) {
    return theChar.codePointAt(5);
  }

  switch (theChar) {
    case "Backspace":
      return 8;
    case "Tab":
      return 9;
    case "Enter":
      return 13;
    case "Alt":
      return 18;
    case "Escape":
      return 27;
    case "Delete":
      return 127;
    case "Minus":
      return 45;
    case "Plus":
      return 43;
    case "Equal":
      return 61;
    case "Delete":
      return 127;
    case "BracketRight":
      return 93;
    case "BracketLeft":
      return 91;
    case "Backslash":
      return 92;
    case "Slash":
      return 47;
    case "Semicolon":
      return 59;
    case "Colon":
      return 58;
    case "Comma":
      return 44;
    case "Period":
      return 46;
    case "Space":
      return 32;
    case "Quote":
      return 34;
    case "Backquote":
      return 39;

    //there are also "Numpad....." variants

    case "Unidentified":
      alert("handle the 'Unidentified' if you want to!");
  }

  /*** there are many other possible character values https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code
        but, AFAIK, there are no unicode code points for them, such as:

  switch (theKey) {
    case "AltLeft":
    case "CapsLock":
    case "ControlRight":
    case "Fn":
    case "NumpadDecimal":
    ...

       event.which may output some number for them, but it is not consistent across
       browsers/machines and they may overlap with other code points.

  ***/

  return 0;
}

//test
document.onkeydown = (event) => {
  console.log('whichSubstitute: ' + whichSubstitute(event) + '; event.which: ' + event.which);
}_

Cette deuxième approche peut être inutile, car la même clé physique peut générer différents caractères Unicode selon différentes dispositions de clavier. Les utilisateurs n'ont peut-être aucune idée de la touche sur laquelle ils doivent appuyer.

Connexes: https://www.w3.org/TR/uievents/#keys

2
flen

De la spécification:

qui de type non signé long, en lecture seule

qui contient un code numérique dépendant du système et de la mise en œuvre signifiant l'identifiant non modifié associé à la touche enfoncée. Dans la plupart des cas, la valeur est identique à keyCode

keyCode de type unsigned long, en lecture seule

keyCode contient un code numérique dépendant du système et de l'implémentation, indiquant l'identifiant non modifié associé à la touche enfoncée. Contrairement à l'attribut KeyboardEvent.key, l'ensemble des valeurs possibles n'est pas défini de manière normative dans cette spécification. En règle générale, ces valeurs de keyCode doivent représenter le point de code décimal dans ASCII [RFC20] [US-ASCII] ou Windows 1252 [WIN1252], mais peuvent être tirées d'un jeu de caractères approprié différent. qui ne sont pas en mesure d'identifier une clé, utilisez la valeur de clé "0".

Voir Modèles de clés hérités pour plus de détails sur la façon de déterminer les valeurs de keyCode.

(J'ai omis les liens)

Il est donc assez facile de créer une version compatible avec la spécification. La version la plus simple renvoie simplement 0 pour chaque touche.

Une version un peu plus impliquée prend event.key et saisit c'est ASCII nombre. Vous pouvez faire de même pour les touches de contrôle (altKey, ctrlKey, shiftKey).

En bref, en l'état, le comportement de which est différent entre les systèmes et les navigateurs. En utilisant les informations d'événement non obsolètes, vous pouvez créer une version plus robuste qui supprime ces différences et sera plus pérenne.

Vous pouvez vérifier le comportement de votre version avec l'implémentation de which dans les principaux navigateurs. Si vous n'utilisez aucun boîtier Edge, votre version sera à la fois simple et compatible.

2
EECOLOR