Remarque: keyCode est désormais obsolète.
La détection de plusieurs touches est facile si vous comprenez le concept
La façon dont je le fais est comme ceci:
var map = {}; // You could also use an array
onkeydown = onkeyup = function(e){
e = e || event; // to deal with IE
map[e.keyCode] = e.type == 'keydown';
/* insert conditional here */
}
Ce code est très simple: comme l'ordinateur ne passe qu'une seule touche à la fois, un tableau est créé pour garder une trace de plusieurs touches. Le tableau peut ensuite être utilisé pour rechercher une ou plusieurs clés à la fois.
Juste pour expliquer, disons que vous appuyez sur Aet B, chacun déclenche un keydown
événement qui prend map[e.keyCode]
la valeur de e.type == keydown
, qui prend la valeur true ou false . Maintenant, les deux map[65]
et map[66]
sont réglés sur true
. Lorsque vous relâchez A
, l' keyup
événement se déclenche, ce qui fait que la même logique détermine le résultat opposé pour map[65]
(A), qui est maintenant faux , mais puisque map[66]
(B) est toujours "down" (il n'a pas déclenché d'événement keyup), cela reste vrai .
Le map
tableau, à travers les deux événements, ressemble à ceci:
// keydown A
// keydown B
[
65:true,
66:true
]
// keyup A
// keydown B
[
65:false,
66:true
]
Il y a deux choses que vous pouvez faire maintenant:
A) Un enregistreur de frappe ( exemple ) peut être créé comme référence pour plus tard lorsque vous souhaitez comprendre rapidement un ou plusieurs codes de clé. En supposant que vous avez défini un élément html et que vous l'avez pointé avec la variable element
.
element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
if(map[i]){
element.innerHTML += '<hr>' + i;
}
}
Remarque: vous pouvez facilement saisir un élément par son id
attribut.
<div id="element"></div>
Cela crée un élément html qui peut être facilement référencé en javascript avec element
alert(element); // [Object HTMLDivElement]
Vous n'avez même pas besoin de l'utiliser document.getElementById()
ou $()
de l'attraper. Mais pour des raisons de compatibilité, l'utilisation de jQuery $()
est plus largement recommandée.
Assurez-vous simplement que la balise de script vient après le corps du HTML. Conseil d'optimisation : la plupart des sites Web de grande renommée placent la balise script après la balise body pour l'optimisation. Cela est dû au fait que la balise de script empêche le chargement d'autres éléments jusqu'à ce que le téléchargement de son script soit terminé. Le placer avant le contenu permet au contenu de se charger à l'avance.
B (qui est là où réside votre intérêt) Vous pouvez vérifier une ou plusieurs clés à la fois où /*insert conditional here*/
était, prenez cet exemple:
if(map[17] && map[16] && map[65]){ // CTRL+SHIFT+A
alert('Control Shift A');
}else if(map[17] && map[16] && map[66]){ // CTRL+SHIFT+B
alert('Control Shift B');
}else if(map[17] && map[16] && map[67]){ // CTRL+SHIFT+C
alert('Control Shift C');
}
Edit : Ce n'est pas l'extrait de code le plus lisible. La lisibilité est importante, vous pouvez donc essayer quelque chose comme ceci pour le rendre plus facile pour les yeux:
function test_key(selkey){
var alias = {
"ctrl": 17,
"shift": 16,
"A": 65,
/* ... */
};
return key[selkey] || key[alias[selkey]];
}
function test_keys(){
var keylist = arguments;
for(var i = 0; i < keylist.length; i++)
if(!test_key(keylist[i]))
return false;
return true;
}
Usage:
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
Est-ce mieux?
if(test_keys('ctrl', 'shift')){
if(test_key('A')){
alert('Control Shift A');
} else if(test_key('B')){
alert('Control Shift B');
} else if(test_key('C')){
alert('Control Shift C');
}
}
(fin de modification)
Cet exemple vérifie pour CtrlShiftA, CtrlShiftBetCtrlShiftC
C'est aussi simple que ça :)
Remarques
Suivi des KeyCodes
En règle générale, il est recommandé de documenter le code, en particulier des éléments tels que les codes clés (comme // CTRL+ENTER
), afin que vous puissiez vous souvenir de ce qu'ils étaient.
Vous devez également mettre les codes clés dans le même ordre que la documentation ( CTRL+ENTER => map[17] && map[13]
, PAS map[13] && map[17]
). De cette façon, vous ne serez jamais confus lorsque vous devez revenir en arrière et modifier le code.
Un gotcha avec des chaînes if-else
Si vous recherchez des combinaisons de quantités différentes (comme CtrlShiftAltEnteret CtrlEnter), placez des combos plus petits après des combos plus grands, sinon les combos plus petits remplaceront les combos plus grands s'ils sont assez similaires. Exemple:
// Correct:
if(map[17] && map[16] && map[13]){ // CTRL+SHIFT+ENTER
alert('Whoa, mr. power user');
}else if(map[17] && map[13]){ // CTRL+ENTER
alert('You found me');
}else if(map[13]){ // ENTER
alert('You pressed Enter. You win the prize!')
}
// Incorrect:
if(map[17] && map[13]){ // CTRL+ENTER
alert('You found me');
}else if(map[17] && map[16] && map[13]){ // CTRL+SHIFT+ENTER
alert('Whoa, mr. power user');
}else if(map[13]){ // ENTER
alert('You pressed Enter. You win the prize!');
}
// What will go wrong: When trying to do CTRL+SHIFT+ENTER, it will
// detect CTRL+ENTER first, and override CTRL+SHIFT+ENTER.
// Removing the else's is not a proper solution, either
// as it will cause it to alert BOTH "Mr. Power user" AND "You Found Me"
Gotcha: "Cette combinaison de touches continue de s'activer même si je n'appuie pas sur les touches"
Lorsque vous traitez des alertes ou de tout ce qui prend le focus de la fenêtre principale, vous souhaiterez peut-être inclure map = []
pour réinitialiser le tableau une fois la condition terminée. C'est parce que certaines choses, comme alert()
, éloignent le focus de la fenêtre principale et empêchent l'événement 'keyup' de se déclencher. Par exemple:
if(map[17] && map[13]){ // CTRL+ENTER
alert('Oh noes, a bug!');
}
// When you Press any key after executing this, it will alert again, even though you
// are clearly NOT pressing CTRL+ENTER
// The fix would look like this:
if(map[17] && map[13]){ // CTRL+ENTER
alert('Take that, bug!');
map = {};
}
// The bug no longer happens since the array is cleared
Gotcha: Paramètres par défaut du navigateur
Voici une chose ennuyeuse que j'ai trouvée, avec la solution incluse:
Problème: étant donné que le navigateur a généralement des actions par défaut sur les combinaisons de touches (comme CtrlDactive la fenêtre des signets ou CtrlShiftCactive skynote sur maxthon), vous pouvez également ajouter return false
après map = []
, afin que les utilisateurs de votre site ne soient pas frustrés lorsque le "fichier en double" fonction, étant mis CtrlD, marque la page à la place.
if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
Sans return false
, la fenêtre Bookmark s'affiche, au grand dam de l'utilisateur.
La déclaration de retour (nouveau)
D'accord, vous ne voulez donc pas toujours quitter la fonction à ce stade. C'est pourquoi la event.preventDefault()
fonction est là. Ce qu'il fait, c'est définir un indicateur interne qui indique à l'interpréteur de ne pas autoriser le navigateur à exécuter son action par défaut. Après cela, l'exécution de la fonction continue (alors qu'elle return
quittera immédiatement la fonction).
Comprenez cette distinction avant de décider d'utiliser return false
ou dee.preventDefault()
event.keyCode
est obsolète
L'utilisateur SeanVieira a souligné dans les commentaires qu'il event.keyCode
est obsolète.
Là, il a donné une excellente alternative:, event.key
qui renvoie une représentation sous forme de chaîne de la touche enfoncée, comme "a"
pour Aou "Shift"
pour Shift.
Je suis allé de l'avant et ai concocté un outil pour examiner lesdites cordes.
element.onevent
contre element.addEventListener
Les gestionnaires enregistrés avec addEventListener
peuvent être empilés et sont appelés dans l'ordre d'enregistrement, tandis que la définition .onevent
directe est plutôt agressive et remplace tout ce que vous aviez auparavant.
document.body.onkeydown = function(ev){
// do some stuff
ev.preventDefault(); // cancels default actions
return false; // cancels this function as well as default actions
}
document.body.addEventListener("keydown", function(ev){
// do some stuff
ev.preventDefault() // cancels default actions
return false; // cancels this function only
});
La .onevent
propriété semble avoir le dessus sur tout et sur le comportement de ev.preventDefault()
et return false;
peut être plutôt imprévisible.
Dans les deux cas, les gestionnaires enregistrés via addEventlistener
semblent être plus faciles à écrire et à raisonner.
Il existe également attachEvent("onevent", callback)
une implémentation non standard d'Internet Explorer, mais cela est au-delà de obsolète et ne concerne même pas JavaScript (il appartient à un langage ésotérique appelé JScript ). Il serait dans votre intérêt d'éviter autant que possible le code polyglotte.
Une classe d'aide
Pour résoudre les problèmes de confusion / plaintes, j'ai écrit une "classe" qui fait cette abstraction ( lien pastebin ):
function Input(el){
var parent = el,
map = {},
intervals = {};
function ev_kdown(ev)
{
map[ev.key] = true;
ev.preventDefault();
return;
}
function ev_kup(ev)
{
map[ev.key] = false;
ev.preventDefault();
return;
}
function key_down(key)
{
return map[key];
}
function keys_down_array(array)
{
for(var i = 0; i < array.length; i++)
if(!key_down(array[i]))
return false;
return true;
}
function keys_down_arguments()
{
return keys_down_array(Array.from(arguments));
}
function clear()
{
map = {};
}
function watch_loop(keylist, callback)
{
return function(){
if(keys_down_array(keylist))
callback();
}
}
function watch(name, callback)
{
var keylist = Array.from(arguments).splice(2);
intervals[name] = setInterval(watch_loop(keylist, callback), 1000/24);
}
function unwatch(name)
{
clearInterval(intervals[name]);
delete intervals[name];
}
function detach()
{
parent.removeEventListener("keydown", ev_kdown);
parent.removeEventListener("keyup", ev_kup);
}
function attach()
{
parent.addEventListener("keydown", ev_kdown);
parent.addEventListener("keyup", ev_kup);
}
function Input()
{
attach();
return {
key_down: key_down,
keys_down: keys_down_arguments,
watch: watch,
unwatch: unwatch,
clear: clear,
detach: detach
};
}
return Input();
}
Cette classe ne fait pas tout et ne gérera pas tous les cas d'utilisation imaginables. Je ne suis pas un bibliothécaire. Mais pour une utilisation interactive générale, cela devrait aller.
Pour utiliser cette classe, créez une instance et pointez-la vers l'élément auquel vous souhaitez associer l'entrée au clavier:
var input_txt = Input(document.getElementById("txt"));
input_txt.watch("print_5", function(){
txt.value += "FIVE ";
}, "Control", "5");
Ce que cela va faire, c'est attacher un nouvel écouteur d'entrée à l'élément avec #txt
(supposons que c'est une zone de texte), et définir un point de surveillance pour la combinaison de touches Ctrl+5
. Lorsque les deux Ctrl
et 5
sont en panne, la fonction de rappel que vous avez passée (dans ce cas, une fonction qui ajoute "FIVE "
à la zone de texte) sera appelée. Le rappel est associé au nom print_5
, donc pour le supprimer, il vous suffit d'utiliser:
input_txt.unwatch("print_5");
Pour se détacher input_txt
de l' txt
élément:
input_txt.detach();
De cette façon, le garbage collection peut récupérer l'objet ( input_txt
), s'il est jeté, et vous n'aurez plus un ancien écouteur d'événement zombie.
Pour plus de précision, voici une référence rapide à l'API de la classe, présentée dans le style C / Java afin que vous sachiez ce qu'ils retournent et quels arguments ils attendent.
Boolean key_down (String key);
Renvoie true
if key
est down, false sinon.
Boolean keys_down (String key1, String key2, ...);
Renvoie true
si toutes les touches key1 .. keyN
sont en bas, false dans le cas contraire.
void watch (String name, Function callback, String key1, String key2, ...);
Crée un "point de surveillance" tel que le fait d'appuyer sur tout keyN
déclenchera le rappel
void unwatch (String name);
Supprime ledit point de surveillance via son nom
void clear (void);
Efface le cache «clés vers le bas». Équivalent à map = {}
ci-dessus
void detach (void);
Détache les écouteurs ev_kdown
et ev_kup
de l'élément parent, ce qui permet de se débarrasser de l'instance en toute sécurité
Mise à jour 02/12/2017 En réponse à une demande de publication sur github, j'ai créé un résumé .
Mise à jour 2018-07-21 Je joue avec la programmation de style déclaratif depuis un certain temps, et c'est maintenant mon préféré: violon , pastebin
Généralement, cela fonctionnera avec les cas que vous voudriez de manière réaliste (ctrl, alt, shift), mais si vous devez appuyer, disons, a+w
en même temps, il ne serait pas trop difficile de "combiner" les approches en un recherche multi-clé.
J'espère que ce mini-blog de réponse bien expliqué a été utile :)