Cri*_*sty 158 javascript keydown
我正在尝试开发一个JavaScript游戏引擎,我遇到了这个问题:
问题是,当我按下右键然后按空格键时,角色跳跃然后停止移动.
我使用该keydown
功能来按下键.如何检查是否一次按下多个键?
Bra*_*est 301
如果您了解这个概念,就可以轻松进行多次击键检测
我这样做的方式是这样的:
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 */
}
Run Code Online (Sandbox Code Playgroud)
此代码非常简单:由于计算机一次只传递一次击键,因此会创建一个数组来跟踪多个键.然后可以使用该阵列一次检查一个或多个密钥.
只是为了说明,假设你按A和B,每触发一个keydown
,设置事件map[e.keyCode]
,它的值e.type == keydown
,其计算结果为真或假.现在,这两个map[65]
并map[66]
设置为true
.当你松开时A
,keyup
事件触发,导致相同的逻辑确定map[65]
(A)的相反结果,现在是假的,但是因为map[66]
(B)仍然"向下"(它没有触发键盘事件),它仍然是真的.
该map
阵列,通过这两个事件,看起来是这样的:
// keydown A
// keydown B
[
65:true,
66:true
]
// keyup A
// keydown B
[
65:false,
66:true
]
Run Code Online (Sandbox Code Playgroud)
你现在可以做两件事:
A)当您想要快速找出一个或多个密钥代码时,可以创建密钥记录器(示例)作为参考.假设您已经定义了一个html元素并使用该变量指向它element
.
element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
if(map[i]){
element.innerHTML += '<hr>' + i;
}
}
Run Code Online (Sandbox Code Playgroud)
注意:您可以通过其id
属性轻松获取元素.
<div id="element"></div>
Run Code Online (Sandbox Code Playgroud)
这将创建一个可以在javascript中轻松引用的html元素 element
alert(element); // [Object HTMLDivElement]
Run Code Online (Sandbox Code Playgroud)
你甚至不必使用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');
}
Run Code Online (Sandbox Code Playgroud)
编辑:这不是最易读的代码段.可读性很重要,所以你可以试试这样的东西让眼睛更容易:
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;
}
Run Code Online (Sandbox Code Playgroud)
用法:
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
Run Code Online (Sandbox Code Playgroud)
这是否更好?
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');
}
}
Run Code Online (Sandbox Code Playgroud)
(编辑结束)
此示例检查CtrlShiftA,CtrlShiftB和CtrlShiftC
就像那一样简单:)
作为一般规则,最好记录代码,特别是密钥代码(如// CTRL+ENTER
),以便您记住它们是什么.
您还应该将密钥代码与文档(CTRL+ENTER => map[17] && map[13]
,NOT map[13] && map[17]
)的顺序相同.这样,当您需要返回并编辑代码时,您将不会感到困惑.
如果检查不同数量的组合(如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"
Run Code Online (Sandbox Code Playgroud)
处理警报或从主窗口获取焦点的任何事物时,您可能希望包括map = []
在条件完成后重置阵列.这是因为有些事情alert()
会使焦点远离主窗口并导致'keyup'事件不会触发.例如:
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
Run Code Online (Sandbox Code Playgroud)
这是我发现的烦人的事情,包括以下解决方案:
问题:由于浏览器通常对键连击默认动作(如CtrlD激活书签窗口,或CtrlShiftC激活上傲游skynote),您可能还需要添加return false
后map = []
,所以你的网站的用户不会感到沮丧时,"重复文件"功能,被戴上CtrlD,书签页面而不是.
if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
Run Code Online (Sandbox Code Playgroud)
没有return false
,书签窗口会弹出,令用户感到沮丧.
好的,所以你并不总是想在那时退出这个功能.这就是event.preventDefault()
函数存在的原因.它的作用是设置一个内部标志,告诉解释,以不使浏览器运行的默认操作.之后,继续执行该功能(而return
将立即退出该功能).
在决定是否使用return false
或之前,请先了解这一区别e.preventDefault()
event.keyCode
已弃用用户SeanVieira在评论中指出了event.keyCode
不推荐使用的内容.
在那里,他提供了一个很好的选择:event.key
它返回被按下的键的字符串表示,如"a"
for A或"Shift"
for 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
});
Run Code Online (Sandbox Code Playgroud)
该.onevent
属性似乎覆盖了一切和行为,ev.preventDefault()
并且return false;
可能是相当不可预测的.
在任何一种情况下,通过注册的处理程序addEventlistener
似乎更容易编写和推理.
还有attachEvent("onevent", callback)
来自Internet Explorer的非标准实现,但这已经被弃用了,甚至不属于JavaScript(它与一种叫做JScript的深奥语言有关).尽可能避免使用多语言代码符合您的最佳利益.
为了解决混淆/抱怨,我写了一个"类"来做这个抽象(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();
}
Run Code Online (Sandbox Code Playgroud)
这个类不会做任何事情,它不会处理每个可能的用例.我不是图书馆的人.但对于一般的交互式使用它应该没问题.
要使用此类,请创建一个实例并将其指向要将键盘输入与之关联的元素:
var input_txt = Input(document.getElementById("txt"));
input_txt.watch("print_5", function(){
txt.value += "FIVE ";
}, "Control", "5");
Run Code Online (Sandbox Code Playgroud)
这将做的是将一个新的输入监听器附加到元素#txt
(让我们假设它是一个textarea),并为关键组合设置一个观察点Ctrl+5
.当两个Ctrl
和5
下降,你传递的回调函数(在这种情况下,增加了一个功能"FIVE "
,以textarea的)将会被调用.回调与名称相关联print_5
,因此要删除它,您只需使用:
input_txt.unwatch("print_5");
Run Code Online (Sandbox Code Playgroud)
要卸下input_txt
从txt
元素:
input_txt.detach();
Run Code Online (Sandbox Code Playgroud)
这样,垃圾收集可以拾取object(input_txt
),如果它被丢弃,你将不会留下旧的僵尸事件监听器.
为了彻底,这里是对类的API的快速参考,以C/Java风格呈现,以便您知道它们返回什么以及它们期望的参数.
Run Code Online (Sandbox Code Playgroud)Boolean key_down (String key);
true
如果key
为down则返回,否则返回false.Run Code Online (Sandbox Code Playgroud)Boolean keys_down (String key1, String key2, ...);
true
如果所有键key1 .. keyN
都关闭则返回,否则返回false.Run Code Online (Sandbox Code Playgroud)void watch (String name, Function callback, String key1, String key2, ...);
创建一个"观察点",按下所有按钮
keyN
将触发回调Run Code Online (Sandbox Code Playgroud)void unwatch (String name);
通过名称删除所述观察点
Run Code Online (Sandbox Code Playgroud)void clear (void);
擦除"按键"缓存.相当于
map = {}
上述Run Code Online (Sandbox Code Playgroud)void detach (void);
将父元素
ev_kdown
与ev_kup
侦听器分离,从而可以安全地删除实例
更新2017-12-02为了回应将此发布到github的请求,我创建了一个要点.
更新2018-07-21我已经玩了一段时间的声明式编程,这种方式现在是我个人的最爱:小提琴,pastebin
一般来说,它会适用于你想要的情况(ctrl,alt,shift),但是如果你需要同时点击a+w
,那么将这些方法"组合"成一个并不太困难.多键查找.
我希望这个彻底解释回答迷你博客很有帮助:)
Mar*_*ijn 30
您应该使用keydown事件来跟踪按下的键,并且您应该使用keyup事件来跟踪释放键的时间.
看到这个例子:http://jsfiddle.net/vor0nwe/mkHsU/
(更新:我在这里复制代码,以防jsfiddle.net保释:) HTML:
<ul id="log">
<li>List of keys:</li>
</ul>
Run Code Online (Sandbox Code Playgroud)
...和Javascript(使用jQuery):
var log = $('#log')[0],
pressedKeys = [];
$(document.body).keydown(function (evt) {
var li = pressedKeys[evt.keyCode];
if (!li) {
li = log.appendChild(document.createElement('li'));
pressedKeys[evt.keyCode] = li;
}
$(li).text('Down: ' + evt.keyCode);
$(li).removeClass('key-up');
});
$(document.body).keyup(function (evt) {
var li = pressedKeys[evt.keyCode];
if (!li) {
li = log.appendChild(document.createElement('li'));
}
$(li).text('Up: ' + evt.keyCode);
$(li).addClass('key-up');
});
Run Code Online (Sandbox Code Playgroud)
在那个例子中,我正在使用一个数组来跟踪正在按下哪些键.在实际应用程序中,delete
一旦释放了相关键,您可能希望使用每个元素.
请注意,虽然我在这个例子中使用了jQuery让自己变得简单,但在使用"原始"Javascript时,这个概念也同样适用.
Edu*_*nda 15
document.onkeydown = keydown;
function keydown (evt) {
if (!evt) evt = event;
if (evt.ctrlKey && evt.altKey && evt.keyCode === 115) {
alert("CTRL+ALT+F4");
} else if (evt.shiftKey && evt.keyCode === 9) {
alert("Shift+TAB");
}
}
Run Code Online (Sandbox Code Playgroud)
我用这种方式(必须检查Shift + Ctrl按下的位置):
// create some object to save all pressed keys
var keys = {
shift: false,
ctrl: false
};
$(document.body).keydown(function(event) {
// save status of the button 'pressed' == 'true'
if (event.keyCode == 16) {
keys["shift"] = true;
} else if (event.keyCode == 17) {
keys["ctrl"] = true;
}
if (keys["shift"] && keys["ctrl"]) {
$("#convert").trigger("click"); // or do anything else
}
});
$(document.body).keyup(function(event) {
// reset status of the button 'released' == 'false'
if (event.keyCode == 16) {
keys["shift"] = false;
} else if (event.keyCode == 17) {
keys["ctrl"] = false;
}
});
Run Code Online (Sandbox Code Playgroud)
谁需要完整的示例代码。右+左添加
var keyPressed = {};
document.addEventListener('keydown', function(e) {
keyPressed[e.key + e.location] = true;
if(keyPressed.Shift1 == true && keyPressed.Control1 == true){
// Left shift+CONTROL pressed!
keyPressed = {}; // reset key map
}
if(keyPressed.Shift2 == true && keyPressed.Control2 == true){
// Right shift+CONTROL pressed!
keyPressed = {};
}
}, false);
document.addEventListener('keyup', function(e) {
keyPressed[e.key + e.location] = false;
keyPressed = {};
}, false);
Run Code Online (Sandbox Code Playgroud)
这不是通用方法,但在某些情况下很有用。它对于CTRL+something或Shift+something或CTRL+ Shift+something等组合很有用。
示例:当您想使用CTRL+打印页面时P,按下的第一个键总是CTRL后跟P。与CTRL+ S、CTRL+U和其他组合相同。
document.addEventListener('keydown',function(e){
//SHIFT + something
if(e.shiftKey){
switch(e.code){
case 'KeyS':
console.log('Shift + S');
break;
}
}
//CTRL + SHIFT + something
if(e.ctrlKey && e.shiftKey){
switch(e.code){
case 'KeyS':
console.log('CTRL + Shift + S');
break;
}
}
});
Run Code Online (Sandbox Code Playgroud)