Hinweis: keyCode ist jetzt veraltet.
Die Erkennung mehrerer Tastenanschläge ist einfach, wenn Sie das Konzept verstehen
So mache ich das:
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 */
}
Dieser Code ist sehr einfach: Da der Computer jeweils nur einen Tastendruck durchläuft, wird ein Array erstellt, um mehrere Schlüssel zu verfolgen. Das Array kann dann verwendet werden, um gleichzeitig nach einem oder mehreren Schlüsseln zu suchen.
Nehmen wir zur Erklärung an, Sie drücken Aund B, jedes löst ein keydown
Ereignis aus, das map[e.keyCode]
auf den Wert von gesetzt e.type == keydown
ist und entweder als wahr oder falsch ausgewertet wird . Jetzt sind beide map[65]
und map[66]
eingestellt auf true
. Wenn Sie loslassen A
, wird das keyup
Ereignis ausgelöst, wodurch dieselbe Logik das entgegengesetzte Ergebnis für map[65]
(A) ermittelt, das jetzt falsch ist , aber da map[66]
(B) immer noch "down" ist (es hat kein Keyup-Ereignis ausgelöst). es bleibt wahr .
Das map
Array sieht bei beiden Ereignissen folgendermaßen aus:
// keydown A
// keydown B
[
65:true,
66:true
]
// keyup A
// keydown B
[
65:false,
66:true
]
Sie können jetzt zwei Dinge tun:
A) Ein Schlüsselprotokollierer ( Beispiel ) kann als Referenz für später erstellt werden, wenn Sie schnell einen oder mehrere Schlüsselcodes herausfinden möchten. Angenommen, Sie haben ein HTML-Element definiert und mit der Variablen darauf verwiesen element
.
element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
if(map[i]){
element.innerHTML += '<hr>' + i;
}
}
Hinweis: Sie können ein Element leicht anhand seines id
Attributs erfassen.
<div id="element"></div>
Dadurch wird ein HTML-Element erstellt, auf das in Javascript leicht verwiesen werden kann element
alert(element); // [Object HTMLDivElement]
Sie müssen es nicht einmal benutzen document.getElementById()
oder $()
greifen. Aus Kompatibilitätsgründen sollten Sie jedoch jQuery's verwenden$()
allgemein empfohlen.
Stellen Sie einfach sicher, dass das Skript- Tag hinter dem HTML-Text steht. Optimierungstipp : Die meisten namhaften Websites setzen das Skript- Tag zur Optimierung nach dem Body-Tag. Dies liegt daran, dass das Skript-Tag das Laden weiterer Elemente blockiert, bis das Herunterladen des Skripts abgeschlossen ist. Wenn Sie es vor den Inhalt stellen, kann der Inhalt zuvor geladen werden.
B (hier liegt Ihr Interesse) Sie können nach einem oder mehreren Schlüsseln gleichzeitig suchen /*insert conditional here*/
. Nehmen Sie dieses Beispiel:
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');
}
Bearbeiten : Das ist nicht das am besten lesbare Snippet. Die Lesbarkeit ist wichtig, daher können Sie so etwas ausprobieren, um die Augen zu schonen:
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;
}
Verwendung:
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
Ist das besser?
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');
}
}
(Ende der Bearbeitung)
In diesem Beispiel wird überprüft CtrlShiftA, CtrlShiftBundCtrlShiftC
So einfach ist das :)
Anmerkungen
KeyCodes im Auge behalten
In der Regel empfiehlt es sich, Code zu dokumentieren, insbesondere Schlüsselcodes (wie // CTRL+ENTER
), damit Sie sich daran erinnern können, was sie waren.
Sie sollten die Schlüsselcodes auch in derselben Reihenfolge wie die Dokumentation CTRL+ENTER => map[17] && map[13]
einfügen ( , NICHT map[13] && map[17]
). Auf diese Weise werden Sie nie verwirrt, wenn Sie zurückgehen und den Code bearbeiten müssen.
Ein Gotcha mit If-else-Ketten
Wenn Sie nach Combos mit unterschiedlichen Beträgen (wie CtrlShiftAltEnterund CtrlEnter) suchen, setzen Sie kleinere Combos nach größeren Combos. Andernfalls überschreiben die kleineren Combos die größeren Combos, wenn sie ähnlich genug sind. Beispiel:
// 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: "Diese Tastenkombination wird weiterhin aktiviert, obwohl ich die Tasten nicht drücke."
Wenn Sie mit Warnungen oder anderen Elementen arbeiten, die den Fokus aus dem Hauptfenster übernehmen, möchten Sie möglicherweise map = []
das Array zurücksetzen, nachdem die Bedingung erfüllt ist. Dies liegt daran, dass einige Dinge, wie z. B. alert()
den Fokus vom Hauptfenster wegnehmen und dazu führen, dass das 'keyup'-Ereignis nicht ausgelöst wird. Beispielsweise:
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: Browser-Standardeinstellungen
Hier ist eine nervige Sache, die ich mit der enthaltenen Lösung gefunden habe:
Problem: Da der Browser normalerweise Standardaktionen für Tastenkombinationen ausführt (z. B. CtrlDdas Lesezeichenfenster CtrlShiftCaktivieren oder Skynote auf maxthon aktivieren), möchten Sie möglicherweise auch return false
nachher hinzufügen map = []
, damit Benutzer Ihrer Website nicht frustriert werden, wenn die "Datei duplizieren" Wenn diese Funktion CtrlDaktiviert ist, wird stattdessen die Seite mit einem Lesezeichen versehen.
if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
Ohne würdereturn false
das Lesezeichenfenster zum Entsetzen des Benutzers auftauchen.
Die return-Anweisung (neu)
Okay, Sie möchten die Funktion an diesem Punkt nicht immer beenden. Deshalb ist die event.preventDefault()
Funktion da. Es wird ein internes Flag gesetzt, das den Interpreter anweist, dem Browser nicht zu erlauben, seine Standardaktion auszuführen. Danach wird die Ausführung der Funktion fortgesetzt (während return
die Funktion sofort beendet wird).
Verstehen Sie diese Unterscheidung, bevor Sie sich für return false
oder entscheidene.preventDefault()
event.keyCode
ist veraltet
Benutzer SeanVieira wies in den Kommentaren darauf hin, dass event.keyCode
veraltet ist.
Dort gab er eine ausgezeichnete Alternative : event.key
, die eine Zeichenfolgendarstellung der gedrückten Taste zurückgibt, wie "a"
für Aoder "Shift"
für Shift.
Ich ging voran und kochte ein Werkzeug, um diese Saiten zu untersuchen.
element.onevent
vs. element.addEventListener
Mit registrierte Handler addEventListener
können gestapelt werden und werden in der Reihenfolge der Registrierung aufgerufen, während die .onevent
direkte Einstellung ziemlich aggressiv ist und alles überschreibt, was Sie zuvor hatten.
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
});
Die .onevent
Eigenschaft scheint alles und das Verhalten von zu überschreiben ev.preventDefault()
und return false;
kann ziemlich unvorhersehbar sein.
In beiden Fällen addEventlistener
scheinen über registrierte Handler einfacher zu schreiben und zu begründen zu sein.
Es gibt auch attachEvent("onevent", callback)
eine nicht standardmäßige Implementierung von Internet Explorer, die jedoch nicht mehr als veraltet ist und sich nicht einmal auf JavaScript bezieht (es handelt sich um eine esoterische Sprache namens JScript ). Es wäre in Ihrem besten Interesse, polyglotten Code so weit wie möglich zu vermeiden.
Eine Helferklasse
Um Verwirrung / Beschwerden zu beheben, habe ich eine "Klasse" geschrieben, die diese Abstraktion ausführt ( Pastebin-Link ):
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();
}
Diese Klasse macht nicht alles und behandelt nicht jeden denkbaren Anwendungsfall. Ich bin kein Bibliothekar. Für den allgemeinen interaktiven Gebrauch sollte dies jedoch in Ordnung sein.
Um diese Klasse zu verwenden, erstellen Sie eine Instanz und zeigen Sie auf das Element, dem Sie Tastatureingaben zuordnen möchten:
var input_txt = Input(document.getElementById("txt"));
input_txt.watch("print_5", function(){
txt.value += "FIVE ";
}, "Control", "5");
Dadurch wird dem Element mit einen neuen Eingabe-Listener hinzugefügt #txt
angehängt (nehmen wir an, es handelt sich um einen Textbereich), und ein Überwachungspunkt für die Tastenkombination festgelegt Ctrl+5
. Wenn beide Ctrl
und nicht 5
verfügbar sind, wird die von Ihnen übergebene Rückruffunktion (in diesem Fall eine Funktion, die "FIVE "
den Textbereich erweitert) aufgerufen. Der Rückruf ist mit dem Namen verknüpft. print_5
Um ihn zu entfernen, verwenden Sie einfach:
input_txt.unwatch("print_5");
So lösen Sie sich input_txt
vom txt
Element:
input_txt.detach();
Auf diese Weise kann die Garbage Collection das Objekt ( input_txt
) aufnehmen, falls es weggeworfen wird, und Sie haben keinen alten Zombie-Ereignis-Listener mehr übrig.
Aus Gründen der Gründlichkeit finden Sie hier eine kurze Referenz zur API der Klasse, die im C / Java-Stil dargestellt wird, damit Sie wissen, was sie zurückgeben und welche Argumente sie erwarten.
Boolean key_down (String key);
Gibt zurück, true
wenn key
down ist, andernfalls false.
Boolean keys_down (String key1, String key2, ...);
Gibt zurück, true
wenn alle Schlüssel gedrückt key1 .. keyN
sind, andernfalls false.
void watch (String name, Function callback, String key1, String key2, ...);
Erstellt einen "Überwachungspunkt", bei dem durch Drücken von "Alle" keyN
der Rückruf ausgelöst wird
void unwatch (String name);
Entfernt den Überwachungspunkt über seinen Namen
void clear (void);
Löscht den Cache "Schlüssel runter". Entspricht map = {}
oben
void detach (void);
Trennt das ev_kdown
und die ev_kup
Listener vom übergeordneten Element, sodass die Instanz sicher entfernt werden kann
Update 2017-12-02 Als Antwort auf eine Anfrage, dies in github zu veröffentlichen, habe ich eine Übersicht erstellt .
Update 2018-07-21 Ich habe eine Weile mit deklarativem Programmieren gespielt und dieser Weg ist jetzt mein persönlicher Favorit: Geige , Pastebin
Im Allgemeinen funktioniert es mit den Fällen, die Sie realistisch möchten (Strg, Alt, Umschalt), aber wenn Sie beispielsweise drücken müssen, a+w
, wäre es nicht allzu schwierig, die Ansätze zu einem zu "kombinieren" Multi-Key-Lookup.
Ich hoffe diese gründlich erklärte Antwort Mini-Blog war hilfreich :)