หมายเหตุ: ขณะนี้ keyCode เลิกใช้แล้ว
การตรวจจับการกดแป้นหลายครั้งเป็นเรื่องง่ายถ้าคุณเข้าใจแนวคิด
วิธีที่ฉันทำมันเป็นเช่นนี้:
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 */
}
รหัสนี้ง่ายมาก: เนื่องจากคอมพิวเตอร์ส่งผ่านการกดแป้นพิมพ์ครั้งละครั้งเท่านั้นอาร์เรย์จึงถูกสร้างขึ้นเพื่อติดตามปุ่มหลาย ๆ ปุ่ม อาเรย์นั้นสามารถใช้เพื่อตรวจสอบหนึ่งคีย์ขึ้นไปในครั้งเดียว
เพียงเพื่ออธิบายสมมติว่าคุณกดAและBแต่ละยิงkeydown
เหตุการณ์ที่ชุดmap[e.keyCode]
ค่าของe.type == keydown
ซึ่งประเมินทั้งจริงหรือเท็จ ตอนนี้ทั้งสองmap[65]
และมีการกำหนดให้map[66]
true
เมื่อคุณปล่อยให้ไปของA
การkeyup
เกิดเพลิงไหม้เหตุการณ์ที่ก่อให้เกิดตรรกะเดียวกันเพื่อตรวจสอบผลตรงข้ามสำหรับmap[65]
(A) ซึ่งขณะนี้เป็นเท็จแต่เนื่องจากmap[66]
(B) ยังคงเป็น "ลง" (มันยังไม่ได้เรียกเหตุการณ์ keyup ก) มันยังคงเป็นจริงเป็นความจริง
map
อาร์เรย์ผ่านเหตุการณ์ทั้งสองมีลักษณะเช่นนี้
// keydown A
// keydown B
[
65:true,
66:true
]
// keyup A
// keydown B
[
65:false,
66:true
]
มีสองสิ่งที่คุณสามารถทำได้ในตอนนี้:
A) Key logger ( ตัวอย่าง ) สามารถสร้างเป็นข้อมูลอ้างอิงได้ในภายหลังเมื่อคุณต้องการหารหัสคีย์อย่างน้อยหนึ่งรหัส สมมติว่าคุณได้กำหนดองค์ประกอบ HTML element
และชี้ไปที่มันมีตัวแปร
element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
if(map[i]){
element.innerHTML += '<hr>' + i;
}
}
หมายเหตุ: คุณสามารถคว้าองค์ประกอบด้วยid
แอตทริบิวต์ได้อย่างง่ายดาย
<div id="element"></div>
สิ่งนี้จะสร้างองค์ประกอบ html ที่สามารถอ้างอิงได้ง่ายใน javascript ด้วย element
alert(element); // [Object HTMLDivElement]
คุณไม่จำเป็นต้องใช้document.getElementById()
หรือ$()
คว้ามัน แต่เพื่อประโยชน์ในการใช้งานร่วมกันได้แนะนำให้ใช้ jQuery $()
มากกว่า
เพียงตรวจสอบให้แน่ใจว่าแท็กสคริปต์มาตามเนื้อความของ HTML เคล็ดลับการเพิ่มประสิทธิภาพ : ส่วนใหญ่เว็บไซต์ขนาดใหญ่ชื่อใส่แท็กสคริปต์ที่หลังแท็ก body เพื่อเพิ่มประสิทธิภาพ นี่เป็นเพราะแท็กสคริปต์บล็อกองค์ประกอบเพิ่มเติมจากการโหลดจนกว่าสคริปต์จะเสร็จสิ้นการดาวน์โหลด การวางไว้ข้างหน้าของเนื้อหาทำให้สามารถโหลดเนื้อหาได้ล่วงหน้า
B (ซึ่งเป็นที่ที่คุณสนใจ)คุณสามารถตรวจสอบหนึ่งหรือมากกว่าหนึ่งคีย์ในเวลาที่/*insert conditional here*/
เป็นตัวอย่าง:
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');
}
แก้ไข : นั่นไม่ใช่ตัวอย่างที่อ่านได้มากที่สุด การอ่านเป็นสิ่งสำคัญดังนั้นคุณสามารถลองทำสิ่งนี้เพื่อทำให้ง่ายต่อสายตา:
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;
}
การใช้งาน:
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
จะดีกว่านี้ไหม
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');
}
}
(สิ้นสุดการแก้ไข)
นี้การตรวจสอบตัวอย่างสำหรับCtrlShiftA, CtrlShiftBและCtrlShiftC
มันง่ายเหมือนที่ :)
หมายเหตุ
การติดตาม KeyCodes
โดยทั่วไปแล้วเป็นวิธีปฏิบัติที่ดีในการจัดทำรหัสเอกสารโดยเฉพาะสิ่งต่าง ๆ เช่นรหัสคีย์ (เช่น // CTRL+ENTER
) เพื่อให้คุณสามารถจดจำสิ่งที่พวกเขาเป็น
คุณควรใส่รหัสที่สำคัญในลำดับเดียวกันกับเอกสาร ( CTRL+ENTER => map[17] && map[13]
ไม่ใช่map[13] && map[17]
) วิธีนี้คุณจะไม่สับสนเมื่อคุณต้องการย้อนกลับและแก้ไขรหัส
gotcha พร้อมโซ่ if-else
หากตรวจสอบคอมโบที่มีจำนวนแตกต่างกัน (เช่นCtrlShiftAltEnterและCtrlEnter) ให้วางคอมโบที่เล็กกว่าหลังคอมโบที่ใหญ่กว่ามิฉะนั้นคอมโบที่เล็กกว่าจะเข้ามาแทนที่คอมโบที่ใหญ่กว่าถ้ามันมีคอมโบเหมือนกัน ตัวอย่าง:
// 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: "คำสั่งผสมคีย์นี้ยังคงเปิดใช้งานแม้ว่าฉันจะไม่ได้กดปุ่ม"
เมื่อจัดการกับการแจ้งเตือนหรือสิ่งใดก็ตามที่โฟกัสจากหน้าต่างหลักคุณอาจต้องการรวมmap = []
การรีเซ็ตอาร์เรย์หลังจากทำตามเงื่อนไข นี่เป็นเพราะบางสิ่งเช่นalert()
ดึงโฟกัสออกไปจากหน้าต่างหลักและทำให้เหตุการณ์ 'การกดปุ่ม' ไม่ทำงาน ตัวอย่างเช่น:
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: ค่าเริ่มต้นของเบราว์เซอร์
นี่คือสิ่งที่น่ารำคาญที่ฉันพบโดยมีวิธีแก้ปัญหารวมอยู่:
ปัญหา: เนื่องจากเบราว์เซอร์มักจะมีการกระทำเริ่มต้นในคีย์คอมโบ (เช่นCtrlDเปิดใช้งานหน้าต่างบุ๊กมาร์กหรือCtrlShiftCเปิดใช้งาน skynote เมื่อ maxthon) คุณอาจต้องการเพิ่มreturn false
หลังจากmap = []
ดังนั้นผู้ใช้เว็บไซต์ของคุณจะไม่ผิดหวังเมื่อ "ซ้ำไฟล์" ฟังก์ชั่นที่ถูกใส่CtrlDบุ๊คมาร์คหน้าแทน
if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
หากไม่มีreturn false
หน้าต่างบุ๊กมาร์กจะปรากฏขึ้นเพื่อทำให้ผู้ใช้ผิดหวัง
คำสั่งการส่งคืน (ใหม่)
ตกลงดังนั้นคุณไม่ต้องการออกจากฟังก์ชั่น ณ เวลานั้นเสมอไป นั่นเป็นเหตุผลที่event.preventDefault()
ฟังก์ชั่นอยู่ที่นั่น สิ่งที่ตั้งค่าการตั้งค่าสถานะภายในที่บอกให้ล่ามไม่อนุญาตให้เบราว์เซอร์เรียกใช้การกระทำเริ่มต้น หลังจากนั้นการทำงานของฟังก์ชันจะดำเนินต่อไป (ในขณะที่return
จะออกจากฟังก์ชันทันที)
ทำความเข้าใจความแตกต่างนี้ก่อนตัดสินใจใช้return false
หรือe.preventDefault()
event.keyCode
เลิกใช้แล้ว
ผู้ใช้SeanVieiraชี้ให้เห็นในความคิดเห็นที่event.keyCode
เลิกใช้แล้ว
ที่นั่นเขาได้ให้ทางเลือกที่ดี: event.key
ซึ่งผลตอบแทนเป็นตัวแทนสตริงของคีย์ถูกกดเช่น"a"
สำหรับAหรือสำหรับ"Shift"
Shift
ฉันไปข้างหน้าและทำเครื่องมือสำหรับการตรวจสอบสตริงดังกล่าว
element.onevent
VS element.addEventListener
ตัวจัดการที่ลงทะเบียนด้วยaddEventListener
สามารถซ้อนกันและถูกเรียกตามลำดับการลงทะเบียนในขณะที่การตั้งค่า.onevent
โดยตรงค่อนข้างก้าวร้าวและแทนที่สิ่งที่คุณเคยมี
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
คุณสมบัติดูเหมือนว่าจะแทนที่ทุกอย่างและลักษณะการทำงานของev.preventDefault()
และreturn false;
สามารถค่อนข้างคาดเดาไม่ได้
ในกรณีใดก็ตามตัวจัดการที่ลงทะเบียนผ่านaddEventlistener
ดูเหมือนจะง่ายต่อการเขียนและเหตุผลเกี่ยวกับ
นอกจากนี้ยังมีattachEvent("onevent", callback)
จากการใช้งานที่ไม่ได้มาตรฐานของ Internet Explorer แต่สิ่งนี้เกินกว่าที่จะปฏิเสธและไม่เกี่ยวข้องกับ JavaScript (เกี่ยวข้องกับภาษาลึกลับที่เรียกว่าJScript ) มันจะเป็นประโยชน์สูงสุดของคุณเพื่อหลีกเลี่ยงรหัส polyglot ให้มากที่สุด
ชั้นผู้ช่วย
เพื่อจัดการกับความสับสน / ข้อร้องเรียนฉันได้เขียน "คลาส" ที่ทำสิ่งนี้เป็นนามธรรม ( ลิงก์ 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();
}
คลาสนี้ไม่ได้ทำทุกอย่างและมันจะไม่จัดการกับทุกกรณีที่นึกออก ฉันไม่ใช่คนห้องสมุด แต่สำหรับการใช้งานแบบอินเตอร์แอคทีฟทั่วไปควรจะใช้ได้
หากต้องการใช้คลาสนี้ให้สร้างอินสแตนซ์และชี้ไปที่องค์ประกอบที่คุณต้องการเชื่อมโยงคีย์บอร์ดเข้าด้วย:
var input_txt = Input(document.getElementById("txt"));
input_txt.watch("print_5", function(){
txt.value += "FIVE ";
}, "Control", "5");
สิ่งนี้จะทำคือการแนบฟังป้อนข้อมูลใหม่ให้กับองค์ประกอบที่มี#txt
(สมมติว่ามันเป็น textarea) และตั้ง watchpoint Ctrl+5
สำหรับคำสั่งผสมที่สำคัญ เมื่อทั้งคู่Ctrl
และ5
ลงฟังก์ชันการโทรกลับที่คุณส่งผ่าน (ในกรณีนี้"FIVE "
จะมีการเรียกฟังก์ชันที่เพิ่มเข้าไปใน textarea) การโทรกลับเชื่อมโยงกับชื่อprint_5
ดังนั้นในการลบคุณเพียงใช้:
input_txt.unwatch("print_5");
ในการแยกออกinput_txt
จากtxt
องค์ประกอบ:
input_txt.detach();
ด้วยวิธีนี้การรวบรวมขยะสามารถเลือกวัตถุ ( input_txt
) ควรโยนทิ้งไปและคุณจะไม่มีผู้ฟังเหตุการณ์ซอมบี้ตัวเก่าเหลืออยู่
เพื่อความทั่วถึงนี่คือการอ้างอิงอย่างรวดเร็วเกี่ยวกับ API ของคลาสที่นำเสนอในสไตล์ C / Java เพื่อให้คุณรู้ว่าพวกเขากลับมาและสิ่งที่พวกเขาคาดหวัง
Boolean key_down (String key);
ผลตอบแทนtrue
ถ้าkey
มันเป็นเท็จเป็นอย่างอื่น
Boolean keys_down (String key1, String key2, ...);
ส่งคืนtrue
ถ้าคีย์ทั้งหมดไม่key1 .. keyN
ถูกต้องเป็นเท็จมิฉะนั้น
void watch (String name, Function callback, String key1, String key2, ...);
สร้าง "watchpoint" ซึ่งการกดทั้งหมดkeyN
จะทำให้เกิดการติดต่อกลับ
void unwatch (String name);
นำ watchpoint ออกจากชื่อของมัน
void clear (void);
เช็ดแคช "keys down" เทียบเท่ากับmap = {}
ข้างบน
void detach (void);
แยกส่วนev_kdown
และev_kup
ผู้ฟังจากองค์ประกอบหลักทำให้สามารถกำจัดอินสแตนซ์ได้อย่างปลอดภัย
ปรับปรุง 2017/12/02ในการตอบสนองต่อการร้องขอในการเผยแพร่นี้เพื่อ GitHub ผมได้สร้างส่วนสำคัญ
อัปเดต 2018-07-21ฉันเคยเล่นกับการเขียนโปรแกรมสไตล์ที่เปิดเผยมาระยะหนึ่งแล้วและตอนนี้วิธีนี้กลายเป็นรายการโปรดส่วนตัวของฉัน: fiddle , pastebin
โดยทั่วไปแล้วมันจะทำงานกับกรณีที่คุณต้องการแนบเนียน (ctrl, alt, shift) แต่ถ้าคุณต้องการตีพูดa+w
ในเวลาเดียวกันมันจะไม่ยากเกินไปที่จะ "รวม" แนวทางเข้าสู่ หลายคีย์ค้นหา
ฉันหวังว่านี้คำตอบที่อธิบายอย่างละเอียดมินิบล็อกมีประโยชน์ :)