Note : keyCode est maintenant déprécié.
La détection des frappes multiples est facile si vous comprenez le concept.
La façon dont je le fais est la suivante :
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 : Puisque l'ordinateur ne laisse passer qu'une seule touche à la fois, un tableau est créé pour garder la trace de plusieurs touches. Le tableau peut ensuite être utilisé pour vérifier une ou plusieurs touches à la fois.
Juste pour expliquer, disons que vous appuyez sur A y B chacun tire un keydown
événement qui fixe map[e.keyCode]
à la valeur de e.type == keydown
qui donne l'une des deux valeurs suivantes vrai o faux . Maintenant, les deux map[65]
y map[66]
sont fixés à true
. Quand vous laissez partir A
le keyup
se déclenche, ce qui amène la même logique à déterminer le résultat opposé pour les éléments suivants map[65]
(A), qui est maintenant faux mais comme map[66]
(B) est toujours "en bas" (il n'a pas déclenché d'événement keyup), il demeure vrai .
El map
Le 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 clés ( exemple ) peut être créé comme référence pour plus tard lorsque vous voulez comprendre rapidement un ou plusieurs codes de clé. En supposant que vous ayez défini un élément html et que vous l'ayez 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 nom. 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 d'utiliser document.getElementById()
o $()
pour le saisir. Mais pour des raisons de compatibilité, l'utilisation de la méthode jQuery $()
est plus largement recommandé.
Assurez-vous simplement que le script vient après le corps du HTML. Conseil d'optimisation : La plupart des sites web de renom mettent la balise script. après la balise body pour l'optimisation. En effet, la balise script bloque le chargement d'autres éléments jusqu'à ce que son script ait fini de se télécharger. Le fait de la placer avant le contenu permet à ce dernier de se charger avant.
B (c'est là que se situe votre intérêt) Vous pouvez vérifier une ou plusieurs clés à la fois dans les cas suivants /*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');
}
Editar : Ce n'est pas le snippet le plus lisible. La lisibilité est importante, vous pouvez donc essayer quelque chose comme ceci pour le rendre plus facile à lire :
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;
}
Utilisation :
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
Est-ce que c'est 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 du montage)
Cet exemple vérifie que CtrlShiftA , CtrlShiftB y CtrlShiftC
C'est aussi simple que cela :)
Notes
Suivi des KeyCodes
En règle générale, il est bon de documenter le code, en particulier les éléments tels que les codes clés (tels que // CTRL+ENTER
) pour que vous puissiez vous souvenir de ce qu'ils étaient.
Vous devez également placer les codes clés dans le même ordre que celui de la documentation ( CTRL+ENTER => map[17] && map[13]
, PAS map[13] && map[17]
). De cette façon, vous ne serez jamais désorienté lorsque vous devrez revenir en arrière et modifier le code.
Un problème avec les chaînes if-else
Si vous vérifiez des combinaisons de montants différents (par exemple CtrlShiftAltEnter y CtrlEnter ), mettre des combos plus petits après les combinaisons plus importantes, ou bien les petites combinaisons remplaceront les grandes combinaisons si elles sont suffisamment 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 à s'activer même si je n'appuie pas sur les touches".
Lorsqu'il s'agit d'alertes ou de tout ce qui détourne l'attention de la fenêtre principale, il peut être utile d'inclure les éléments suivants map = []
pour réinitialiser le tableau après la réalisation de la condition. Cela est dû au fait que certaines choses, comme alert()
En effet, si l'événement "keyup" n'est pas déclenché, la fenêtre principale n'est pas mise en évidence. 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
Gaffe : les 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 (telles que CtrlD active la fenêtre des signets, ou CtrlShiftC active skynote sur maxthon), vous pourriez aussi ajouter return false
après map = []
Ainsi, les utilisateurs de votre site ne seront pas frustrés lorsque la fonction "Fichier dupliqué", qui est mise en place sur le site de la Commission européenne, sera désactivée. CtrlD pour mettre la page en signet.
if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
Sans return false
la fenêtre Signets serait apparaissent, au grand dam de l'utilisateur.
La déclaration de retour (nouveau)
Ok, donc vous ne voulez pas toujours quitter la fonction à ce moment-là. C'est pourquoi le event.preventDefault()
La fonction est là. Ce qu'elle fait, c'est mettre un drapeau interne qui dit à l'interpréteur de no permettre au navigateur d'exécuter son action par défaut. Après cela, l'exécution de la fonction se poursuit (alors que return
quittera immédiatement la fonction).
Comprenez bien cette distinction avant de décider si vous devez utiliser return false
o e.preventDefault()
event.keyCode
est déprécié
Utilisateur SeanVieira a souligné dans les commentaires que event.keyCode
est déprécié.
Là, il a donné une excellente alternative : event.key
qui renvoie une représentation sous forme de chaîne de caractères de la touche enfoncée, comme suit "a"
para A ou "Shift"
para Shift .
J'ai pris les devants et j'ai préparé un outil pour examiner lesdites cordes.
element.onevent
vs element.addEventListener
Manipulateurs enregistrés auprès de addEventListener
peuvent être empilés, et sont appelés dans l'ordre d'enregistrement, tandis que le réglage .onevent
directement est plutôt agressive et remplace tout ce que vous possédiez précédemment.
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
});
El .onevent
semble tout remplacer et le comportement de la propriété ev.preventDefault()
y return false;
peuvent être assez imprévisibles.
Dans les deux cas, les gestionnaires enregistrés via addEventlistener
semblent plus faciles à écrire et à raisonner.
Il existe également attachEvent("onevent", callback)
à partir de l'implémentation non standard d'Internet Explorer, mais cela est plus que déprécié et ne concerne même pas JavaScript (il s'agit d'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épondre à la confusion/aux 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ère pas tous les cas d'utilisation imaginables. Je ne suis pas un spécialiste des bibliothèques. Mais pour une utilisation interactive générale, elle devrait convenir.
Pour utiliser cette classe, créez une instance et faites-la pointer vers l'élément auquel vous souhaitez associer la saisie 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 nouveau récepteur d'entrée à l'élément avec #txt
(supposons qu'il s'agit d'un textarea), et définir un point de surveillance pour la combinaison de touches Ctrl+5
. Lorsque les deux Ctrl
y 5
sont désactivées, la fonction de rappel que vous avez transmise (dans ce cas, une fonction qui ajoute des données à la base de données de l'entreprise) est activée. "FIVE "
à la zone de texte) sera appelée. La callback est associée au nom print_5
donc pour le supprimer, il suffit d'utiliser :
input_txt.unwatch("print_5");
Pour détacher input_txt
de la txt
élément :
input_txt.detach();
De cette façon, le ramassage des ordures peut récupérer l'objet ( input_txt
), s'il est jeté, et vous n'aurez plus un vieil écouteur d'événements zombie.
Par souci d'exhaustivité, voici une référence rapide aux API des classes, présentées en style C/Java afin que vous sachiez ce qu'elles retournent et quels arguments elles attendent.
Boolean key_down (String key);
Renvoie à true
si key
est en panne, faux sinon.
Boolean keys_down (String key1, String key2, ...);
Renvoie à true
si toutes les clés key1 .. keyN
sont en panne, faux sinon.
void watch (String name, Function callback, String key1, String key2, ...);
Crée un "point de surveillance" tel que le fait d'appuyer sur toutes les keyN
déclenchera le callback
void unwatch (String name);
Supprime ledit point de surveillance via son nom
void clear (void);
Efface le cache des "clés". Equivalent à map = {}
au-dessus de
void detach (void);
Détache le ev_kdown
y ev_kup
de l'élément parent, ce qui permet de se débarrasser en toute sécurité de l'instance
Mise à jour 2017-12-02 En réponse à une demande de publier ceci sur github, j'ai créé une Gist .
Mise à jour 2018-07-21 Je joue avec la programmation de style déclaratif depuis un certain temps, et cette méthode est maintenant ma préférée : violon , pastebin
En règle générale, cela fonctionne avec les cas que vous souhaitez (ctrl, alt, shift), mais si vous avez besoin de frapper, par exemple, a+w
en même temps, il ne serait pas trop difficile de "combiner" les approches dans une recherche à touches multiples.
J'espère que ce réponse expliquée en détail Le mini-blog a été utile :)
3 votes
Voici une démonstration d'une page Web qui imprime automatiquement une liste de toutes les touches enfoncées : stackoverflow.com/a/13651016/975097