Catatan: keyCode sekarang sudah tidak digunakan lagi.
Deteksi beberapa keystroke mudah jika Anda memahami konsepnya
Cara saya melakukannya adalah seperti ini:
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 */
}
Kode ini sangat sederhana: Karena komputer hanya melewati satu penekanan tombol pada satu waktu, sebuah array dibuat untuk melacak beberapa kunci. Array kemudian dapat digunakan untuk memeriksa satu atau lebih kunci sekaligus.
Hanya untuk menjelaskan, katakanlah Anda menekan Adan B, masing-masing menembakkan keydown
peristiwa yang menetapkan map[e.keyCode]
nilai e.type == keydown
, yang mengevaluasi benar atau salah . Sekarang keduanya map[65]
dan map[66]
diatur ke true
. Ketika Anda melepaskan A
, keyup
acara menyala, menyebabkan logika yang sama untuk menentukan hasil yang berlawanan untuk map[65]
(A), yang sekarang salah , tetapi karena map[66]
(B) masih "turun" (itu belum memicu peristiwa keyup), itu tetap benar .
The map
array, melalui acara, terlihat seperti ini:
// keydown A
// keydown B
[
65:true,
66:true
]
// keyup A
// keydown B
[
65:false,
66:true
]
Ada dua hal yang dapat Anda lakukan sekarang:
A) Pencatat Kunci ( contoh ) dapat dibuat sebagai referensi untuk nanti ketika Anda ingin mencari satu atau lebih kode kunci dengan cepat. Dengan asumsi Anda telah mendefinisikan elemen html dan menunjuknya dengan variabel element
.
element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
if(map[i]){
element.innerHTML += '<hr>' + i;
}
}
Catatan: Anda dapat dengan mudah mengambil elemen dengan id
atributnya.
<div id="element"></div>
Ini menciptakan elemen html yang dapat dengan mudah direferensikan dalam javascript dengan element
alert(element); // [Object HTMLDivElement]
Anda bahkan tidak perlu menggunakan document.getElementById()
atau $()
mengambilnya. Tetapi demi kompatibilitas, penggunaan jQuery $()
lebih banyak direkomendasikan.
Pastikan tag skrip muncul setelah badan HTML. Kiat pengoptimalan : Sebagian besar situs web dengan nama besar memasang tag skrip setelah tag badan untuk pengoptimalan. Ini karena tag skrip memblokir elemen lebih lanjut dari pemuatan hingga skripnya selesai diunduh. Menempatkannya di depan konten memungkinkan konten untuk memuat sebelumnya.
B (di mana minat Anda terletak) Anda dapat memeriksa satu atau lebih kunci pada saat /*insert conditional here*/
itu, ambil contoh ini:
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');
}
Sunting : Itu bukan cuplikan yang paling mudah dibaca. Keterbacaan penting, sehingga Anda dapat mencoba sesuatu seperti ini untuk memudahkan mata:
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;
}
Pemakaian:
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
Ini lebih baik?
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');
}
}
(akhir suntingan)
Contoh ini memeriksa CtrlShiftA, CtrlShiftBdanCtrlShiftC
Ini sesederhana itu :)
Catatan
Melacak Kode Kunci
Sebagai aturan umum, adalah praktik yang baik untuk mendokumentasikan kode, terutama hal-hal seperti Kode kunci (seperti // CTRL+ENTER
) sehingga Anda dapat mengingat apa itu.
Anda juga harus meletakkan kode kunci dalam urutan yang sama dengan dokumentasi ( CTRL+ENTER => map[17] && map[13]
, BUKAN map[13] && map[17]
). Dengan cara ini Anda tidak akan pernah bingung ketika Anda perlu kembali dan mengedit kode.
Gotcha dengan rantai if-else
Jika memeriksa kombo dengan jumlah berbeda (seperti CtrlShiftAltEnterdan CtrlEnter), masukkan kombo yang lebih kecil setelah kombo yang lebih besar, atau kombo yang lebih kecil akan mengesampingkan kombo yang lebih besar jika mereka cukup mirip. Contoh:
// 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: "Kombo kunci ini tetap aktif meskipun saya tidak menekan tombol"
Saat berurusan dengan peringatan atau apa pun yang mengambil fokus dari jendela utama, Anda mungkin ingin menyertakan map = []
untuk mengatur ulang array setelah kondisi selesai. Ini karena beberapa hal, seperti alert()
, mengalihkan fokus dari jendela utama dan menyebabkan acara 'keyup' tidak terpicu. Sebagai contoh:
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: Default Peramban
Inilah hal yang menjengkelkan yang saya temukan, termasuk solusinya:
Masalah: Karena browser biasanya memiliki tindakan default pada kombo kunci (seperti CtrlDmengaktifkan jendela bookmark, atau CtrlShiftCmengaktifkan skynote di maxthon), Anda mungkin juga ingin menambahkan return false
setelah map = []
, sehingga pengguna situs Anda tidak akan frustrasi ketika "Duplicate File" fungsi, sedang memakai CtrlD, bookmark halaman sebagai gantinya.
if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
Tanpa return false
, jendela Bookmark akan muncul, yang membuat pengguna kecewa.
Pernyataan pengembalian (baru)
Oke, jadi Anda tidak selalu ingin keluar dari fungsi pada saat itu. Itu sebabnya event.preventDefault()
fungsinya ada di sana. Apa yang dilakukannya adalah menetapkan bendera internal yang memberi tahu juru bahasa untuk tidak mengizinkan browser untuk menjalankan tindakan standarnya. Setelah itu, eksekusi fungsi berlanjut (padahal return
akan segera keluar fungsi).
Pahami perbedaan ini sebelum Anda memutuskan apakah akan menggunakan return false
ataue.preventDefault()
event.keyCode
sudah ditinggalkan
Pengguna SeanVieira menunjukkan dalam komentar yang event.keyCode
sudah usang.
Di sana, ia memberikan alternatif yang sangat baik:, event.key
yang mengembalikan representasi string dari tombol yang ditekan, seperti "a"
untuk A, atau "Shift"
untukShift .
Saya pergi ke depan dan memasak alat untuk memeriksa string tersebut.
element.onevent
vs. element.addEventListener
Penangan yang terdaftar addEventListener
dapat ditumpuk, dan dipanggil dalam urutan pendaftaran, sementara pengaturan .onevent
langsung agak agresif dan menimpa apa pun yang sebelumnya Anda miliki.
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
});
The .onevent
properti tampaknya menimpa segala sesuatu dan perilaku ev.preventDefault()
danreturn false;
bisa agak tak terduga.
Dalam kedua kasus, penangan terdaftar melalui addEventlistener
tampaknya lebih mudah untuk menulis dan mempertimbangkannya.
Ada juga attachEvent("onevent", callback)
dari implementasi non-standar Internet Explorer, tetapi ini sudah usang dan bahkan tidak berkaitan dengan JavaScript (itu berkaitan dengan bahasa esoterik yang disebut JScript ). Akan menjadi kepentingan terbaik Anda untuk menghindari kode polyglot sebanyak mungkin.
Kelas pembantu
Untuk mengatasi kebingungan / keluhan, saya telah menulis "kelas" yang melakukan abstraksi ini ( tautan 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();
}
Kelas ini tidak melakukan segalanya dan tidak akan menangani setiap kasus penggunaan yang mungkin. Saya bukan orang perpustakaan. Tetapi untuk penggunaan interaktif umum itu harus baik-baik saja.
Untuk menggunakan kelas ini, buat instance dan arahkan ke elemen yang ingin Anda kaitkan dengan input keyboard:
var input_txt = Input(document.getElementById("txt"));
input_txt.watch("print_5", function(){
txt.value += "FIVE ";
}, "Control", "5");
Apa yang akan dilakukan adalah melampirkan pendengar input baru ke elemen dengan #txt
(mari kita asumsikan itu adalah textarea), dan menetapkan titik pantauan untuk kombo kunci Ctrl+5
. Saat keduanya Ctrl
dan 5
sedang turun, fungsi panggilan balik yang Anda berikan (dalam hal ini, fungsi yang menambah "FIVE "
textarea) akan dipanggil. Callback dikaitkan dengan nama print_5
, jadi untuk menghapusnya, Anda cukup menggunakan:
input_txt.unwatch("print_5");
Untuk melepaskan input_txt
dari txt
elemen:
input_txt.detach();
Dengan cara ini, pengumpulan sampah dapat mengambil objek ( input_txt
), harus dibuang, dan Anda tidak akan memiliki pendengar acara zombie yang tersisa.
Untuk ketelitian, berikut adalah referensi cepat ke API kelas, disajikan dalam gaya C / Java sehingga Anda tahu apa yang mereka kembalikan dan argumen apa yang mereka harapkan.
Boolean key_down (String key);
Mengembalikan true
jika key
down, false jika tidak.
Boolean keys_down (String key1, String key2, ...);
Kembali true
jika semua kunci key1 .. keyN
turun, salah jika sebaliknya.
void watch (String name, Function callback, String key1, String key2, ...);
Menciptakan "titik pandang" sehingga menekan semua keyN
akan memicu panggilan balik
void unwatch (String name);
Menghapus kata titik pantauan melalui namanya
void clear (void);
Usap cache "kunci ke bawah". Setara dengan di map = {}
atas
void detach (void);
Lepaskan ev_kdown
dan ev_kup
pendengar dari elemen induk, sehingga memungkinkan untuk menyingkirkan instance secara aman
Pembaruan 2017-12-02 Menanggapi permintaan untuk menerbitkan ini ke github, saya telah membuat inti .
Pembaruan 2018-07-21 Saya sudah bermain dengan pemrograman gaya deklaratif untuk sementara waktu, dan cara ini sekarang menjadi favorit pribadi saya: biola , pastebin
Secara umum, ini akan bekerja dengan case yang Anda inginkan secara realistis (ctrl, alt, shift), tetapi jika Anda perlu menekan, katakanlah, a+w
pada saat yang sama, itu tidak akan terlalu sulit untuk "menggabungkan" pendekatan menjadi sebuah pencarian multi-kunci.
Saya harap jawaban yang dijelaskan di blog ini sangat membantu :)