Not: keyCode artık kullanımdan kaldırılmıştır.
Konsepti anlarsanız çoklu tuş vuruşu algılaması kolaydır
Ben böyle yapmak:
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 */
}
Bu kod çok basittir: Bilgisayar bir seferde yalnızca bir tuşa bastığından, birden fazla anahtarı izlemek için bir dizi oluşturulur. Dizi daha sonra bir kerede bir veya daha fazla anahtarı kontrol etmek için kullanılabilir.
Sadece açıklamak için, diyelim ki bastığınız Ave Bher biri değerine keydown
ayarlanan , doğru veya yanlış olarak değerlendirilen bir olayı tetikler . Şimdi her ikisi de ve olarak ayarlanmıştır . Eğer gitmesine izin verdiğinde , için ters sonucunu belirlemeye aynı mantık neden olay yangınları artık (A), yanlış , ancak o zamandan beri (B) "aşağı" (bir keyup olayı tetikleyen henüz) hala, gerçek kalır .map[e.keyCode]
e.type == keydown
map[65]
map[66]
true
A
keyup
map[65]
map[66]
map
Dizi, hem etkinlikler aracılığıyla, şöyle görünür:
// keydown A
// keydown B
[
65:true,
66:true
]
// keyup A
// keydown B
[
65:false,
66:true
]
Şimdi yapabileceğiniz iki şey var:
A) Bir veya daha fazla tuş kodunu hızlı bir şekilde bulmak istediğinizde, bir Key logger ( örnek ) daha sonra referans olarak oluşturulabilir. Bir html öğesi tanımladığınızı ve değişkenle işaret ettiğinizi varsayarsak element
.
element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
if(map[i]){
element.innerHTML += '<hr>' + i;
}
}
Not: Bir öğeyi id
özelliğine göre kolayca yakalayabilirsiniz .
<div id="element"></div>
Bu, javascript ile kolayca referans verilebilecek bir html öğesi oluşturur element
alert(element); // [Object HTMLDivElement]
Kullanmak document.getElementById()
ya $()
da kapmak zorunda bile değilsin . Ancak uyumluluk uğruna, jQuery's $()
kullanımı daha yaygın olarak önerilmektedir.
Komut dosyası etiketinin HTML gövdesinden sonra geldiğinden emin olun . Optimizasyon ipucu : Çoğu büyük isim web siteleri komut dosyası etiketi koymak sonra optimizasyonu için gövde etiketinin. Bunun nedeni, komut dosyası etiketinin, komut dosyasının indirilmesi bitinceye kadar diğer öğelerin yüklenmesini engellemesidir. İçeriğin önüne koymak, içeriğin önceden yüklenmesini sağlar.
B (ilginizi çeken yer burası) Bir seferde bir veya daha fazla anahtarı kontrol edebilirsiniz /*insert conditional here*/
, bu örneği alın:
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');
}
Düzenleme : Bu en okunabilir snippet değildir. Okunabilirlik önemlidir, böylece gözlerinizi kolaylaştırmak için böyle bir şey deneyebilirsiniz:
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;
}
Kullanımı:
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
Bu daha mı iyi?
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');
}
}
(düzenleme sonu)
Bu örnek, kontrol için CtrlShiftA, CtrlShiftBveCtrlShiftC
Bu kadar basit :)
notlar
Anahtar Kodları Takip Etme
Genel bir kural olarak, kodu, özellikle de Anahtar kodları (gibi // CTRL+ENTER
) belgelemek iyi bir uygulamadır, böylece ne olduklarını hatırlayabilirsiniz.
Ayrıca anahtar kodlarını dokümantasyonla aynı sıraya koymalısınız ( CTRL+ENTER => map[17] && map[13]
, NOT map[13] && map[17]
). Bu şekilde, geri dönüp kodu düzenlemeniz gerektiğinde kafanız karışmaz.
If-else zincirleri olan bir gotcha
Farklı miktarlarda ( CtrlShiftAltEnterve gibi CtrlEnter) kombinasyonları kontrol ediyorsanız , daha büyük kombinasyonlardan sonra daha küçük kombinasyonlar koyun , aksi takdirde daha küçük kombinasyonlar, yeterince benzer olmaları durumunda daha büyük kombinasyonları geçersiz kılar. Misal:
// 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: "Tuşlara basmamama rağmen bu tuş kombinasyonu aktif olmaya devam ediyor"
Uyarılar veya ana pencereden odaklanan herhangi bir şeyle uğraşırken map = []
, koşul tamamlandıktan sonra diziyi sıfırlamayı dahil etmek isteyebilirsiniz . Bunun nedeni, bazı şeylerin alert()
odağı ana pencereden uzaklaştırması ve 'keyup' olayının tetiklenmemesine neden olmasıdır. Örneğin:
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
Yakaladım: Tarayıcı Varsayılanları
İşte çözüm dahil, bulduğum can sıkıcı bir şey:
Sorun: Tarayıcı genellikle tuş kombinasyonlarında varsayılan işlemlere sahip olduğundan ( CtrlDyer işareti penceresini CtrlShiftCetkinleştirdiğinde veya maxthon'da skynote'u etkinleştirdiğinde), return false
sonradan eklemek de isteyebilirsiniz map = []
, bu nedenle "Yinelenen Dosya" işlevi, CtrlDkoyuluyor, bunun yerine sayfaya yer işareti koyar .
if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
Olmadan return false
, İmi penceresi olacağını kullanıcının dehşet, açılır.
İade beyanı (yeni)
Tamam, bu yüzden bu noktada işlevden her zaman çıkmak istemezsiniz. Bu yüzden event.preventDefault()
fonksiyon orada. Yaptığı şey için tercüman söyleyen bir iç bayrak ayarlanır değil tarayıcı varsayılan eylemi çalışmasına izin verin. Bundan sonra, işlevin yürütülmesi devam eder (oysa return
işlevden hemen çıkılır).
Kullanıp kullanmamaya karar vermeden önce bu ayrımı anlayın return false
veyae.preventDefault()
event.keyCode
kullanımdan kaldırıldı
Kullanıcı SeanVieiraevent.keyCode
, kullanımdan kaldırılan yorumlara dikkat çekti .
Orada, mükemmel bir alternatif verdi: event.key
basılan tuşun dize ile temsil edilen halini döndürür "a"
.A veya "Shift"
içinShift .
Devam ettim ve pişirdim söz konusu ipleri incelemek araç hazırladım.
element.onevent
vs element.addEventListener
Kayıtlı işleyiciler addEventListener
istiflenebilir ve kayıt sırasına göre çağrılırken .onevent
doğrudan ayar oldukça agresiftir ve daha önce sahip olduğunuz her şeyi geçersiz kılar.
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
});
.onevent
Mülkiyet herşeyi ve davranışını geçersiz görünüyor ev.preventDefault()
vereturn false;
oldukça öngörülemeyen olabilir.
Her iki durumda da, addEventlistener
yazılması ve nedeni hakkında daha kolay olduğu görülmektedir.
Orada da attachEvent("onevent", callback)
Internet Explorer'ın standart dışı uygulanmasından, ancak bu kullanım dışı ötesinde ve hatta JavaScript ilgisi bulunmayan (o denilen ezoterik dili ile ilgilidir JScript ). Polyglot kodundan mümkün olduğunca kaçınmak sizin yararınıza olacaktır.
Bir yardımcı sınıf
Karışıklık / şikayetleri ele almak için, bu soyutlamayı yapan bir "sınıf" yazdım ( pastebin bağlantısı ):
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();
}
Bu sınıf her şeyi yapmaz ve akla gelebilecek her kullanım durumunu ele almaz. Ben kütüphane adamı değilim. Ancak genel interaktif kullanım için iyi olmalı.
Bu sınıfı kullanmak için bir örnek oluşturun ve bunu klavye girdisini ilişkilendirmek istediğiniz öğeye yönlendirin:
var input_txt = Input(document.getElementById("txt"));
input_txt.watch("print_5", function(){
txt.value += "FIVE ";
}, "Control", "5");
Bunun yapacağı şey, öğeye yeni bir giriş dinleyicisi eklemek #txt
(bir metin alanı olduğunu varsayalım) ve tuş kombo için bir izleme noktası ayarlamaktır Ctrl+5
. Ne zaman hem Ctrl
ve 5
aşağı, sen geçirilen geri arama işlevi (bu durumda, ekler bir işlev "FIVE "
textarea kadar) çağrılır. Geri arama adla ilişkilidir print_5
, bu nedenle kaldırmak için şunları kullanırsınız:
input_txt.unwatch("print_5");
Öğeden ayırmak input_txt
için txt
:
input_txt.detach();
Bu şekilde, çöp toplama input_txt
atılırsa nesneyi ( ) alabilir ve eski bir zombi olay dinleyiciniz kalmaz.
Kapsamlı olmak için, C / Java stilinde sunulan sınıf API'sine hızlı bir başvuru, böylece ne döndüklerini ve hangi argümanları beklediklerini biliyorsunuz.
Boolean key_down (String key);
İade true
eğer key
aksi halde false, aşağıda.
Boolean keys_down (String key1, String key2, ...);
İade true
bütün tuşlar eğer key1 .. keyN
aksi halde false, kesik.
void watch (String name, Function callback, String key1, String key2, ...);
Tümüne basmak keyN
geri aramayı tetikleyecek bir "izleme noktası" oluşturur
void unwatch (String name);
Adı geçen izleme noktasını adıyla kaldırır
void clear (void);
"Keys down" önbelleğini siler. map = {}
Yukarıya eşdeğer
void detach (void);
Üst öğeden ev_kdown
ve ev_kup
dinleyicileri ayırır , böylece örnekten güvenli bir şekilde kurtulmayı mümkün kılar
Güncelleme 2017/12/02 Github için bu yayınlamak için bir isteğe yanıt olarak, bir oluşturduk özünü .
Güncelleme 2018-07-21 Bir süredir deklaratif stil programlama ile oynuyorum ve bu şekilde artık benim kişisel favorim: keman , macun
Genellikle, gerçekçi olarak isteyeceğiniz vakalarla (ctrl, alt, shift) çalışır, ancak a+w
aynı anda vurmak, söylemek gerekirse, yaklaşımları bir çoklu anahtar araması.
Umarım bu iyice açıklanmış cevap mini blog yardımcı oldu :)