如何使用 JavaScript 检测是否一次按下多个键?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5203407/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:09:57  来源:igfitidea点击:

How to detect if multiple keys are pressed at once using JavaScript?

javascriptkeydown

提问by XCS

I'm trying to develop a JavaScript game engine and I've came across this problem:

我正在尝试开发一个 JavaScript 游戏引擎,但遇到了这个问题:

  • When I press SPACEthe character jumps.
  • When I press the character moves right.
  • 当我按下SPACE字符跳跃。
  • 当我按下字符向右移动时。

The problem is that when I'm pressing right and then press space, the character jumps and then stops moving.

问题是当我按右键然后按空格键时,角色会跳跃然后停止移动。

I use the keydownfunction to get the key pressed. How can I check if there are multiple keys pressed at once?

我使用该keydown函数来按下键。如何检查是否同时按下了多个键?

回答by Braden Best

Note: keyCode is now deprecated.

注意:keyCode 现在已弃用。

Multiple keystroke detection is easy if you understand the concept

如果您了解这个概念,则多次击键检测很容易

The way I do it is like this:

我的做法是这样的:

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 */
}

This code is very simple: Since the computer only passes one keystroke at a time, an array is created to keep track of multiple keys. The array can then be used to check for one or more keys at once.

这段代码非常简单:由于计算机一次只传递一个按键,因此创建了一个数组来跟踪多个按键。然后可以使用该数组一次检查一个或多个键。

Just to explain, let's say you press Aand B, each fires a keydownevent that sets map[e.keyCode]to the value of e.type == keydown, which evaluates to either trueor false. Now both map[65]and map[66]are set to true. When you let go of A, the keyupevent fires, causing the same logic to determine the opposite result for map[65](A), which is now false, but since map[66](B) is still "down" (it hasn't triggered a keyup event), it remains true.

只是为了解释一下,假设您按AB,每个都会触发一个keydown事件,该事件设置map[e.keyCode]为 的值e.type == keydown,该事件的计算结果为truefalse。现在两者map[65]map[66]都设置为true。当你放开 时Akeyup事件会触发,导致相同的逻辑来确定map[65](A)的相反结果,现在是false,但由于map[66](B) 仍然“向下”(它没有触发 keyup 事件),它仍然如此

The maparray, through both events, looks like this:

map阵列,通过这两个事件,看起来是这样的:

// keydown A 
// keydown B
[
    65:true,
    66:true
]
// keyup A
// keydown B
[
    65:false,
    66:true
]

There are two things you can do now:

您现在可以做两件事:

A)A Key logger (example) can be created as a reference for later when you want to quickly figure out one or more key codes. Assuming you have defined an html element and pointed to it with the variable element.

A)当您想快速找出一个或多个键代码时,可以创建一个键记录器(示例)作为参考。假设您已经定义了一个 html 元素并使用变量 指向它element

element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
    if(map[i]){
        element.innerHTML += '<hr>' + i;
    }
}

Note: You can easily grab an element by its idattribute.

注意:您可以通过id属性轻松获取元素。

<div id="element"></div>

This creates an html element that can be easily referenced in javascript with element

这将创建一个可以在 javascript 中轻松引用的 html 元素 element

alert(element); // [Object HTMLDivElement]

You don't even have to use document.getElementById()or $()to grab it. But for the sake of compatibility, use of jQuery's $()is more widely recommended.

您甚至不必使用document.getElementById()$()抓住它。但是为了兼容性,$()更广泛地推荐使用 jQuery 。

Just make sure the scripttag comes after the body of the HTML. Optimization tip: Most big-name websites put the script tag afterthe body tag for optimization. This is because the script tag blocks further elements from loading until its script is finished downloading. Putting it ahead of the content allows the content to load beforehand.

只需确保脚本标记位于 HTML 正文之后。优化提示:大多数大牌网站把脚本标签body标签进行优化。这是因为脚本标签会阻止加载更多元素,直到其脚本完成下载。将它放在内容之前允许内容提前加载。

B (which is where your interest lies)You can check for one or more keys at a time where /*insert conditional here*/was, take this example:

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');
}


Edit: That isn't the most readable snippet. Readability's important, so you could try something like this to make it easier on the eyes:

编辑:这不是最易读的片段。可读性很重要,所以你可以尝试这样的事情来让眼睛更容易:

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;
}

Usage:

用法:

test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')

Is this better?

这是否更好?

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');
    }
}

(end of edit)

(编辑结束)



This example checks for CtrlShiftA, CtrlShiftB, and CtrlShiftC

本实施例中检查CtrlShiftACtrlShiftBCtrlShiftC

It's just as simple as that :)

就这么简单:)

Notes

笔记

Keeping Track of KeyCodes

跟踪密钥代码

As a general rule, it is good practice to document code, especially things like Key codes (like // CTRL+ENTER) so you can remember what they were.

作为一般规则,记录代码是一种很好的做法,尤其是像 Key 代码(如// CTRL+ENTER)这样的东西,这样你就可以记住它们是什么。

You should also put the key codes in the same order as the documentation (CTRL+ENTER => map[17] && map[13], NOT map[13] && map[17]). This way you won't ever get confused when you need to go back and edit the code.

您还应该按照与文档相同的顺序放置密钥代码 ( CTRL+ENTER => map[17] && map[13], NOT map[13] && map[17])。这样,当您需要返回并编辑代码时,您将永远不会感到困惑。

A gotcha with if-else chains

一个带有 if-else 链的陷阱

If checking for combos of differing amounts (like CtrlShiftAltEnterand CtrlEnter), put smaller combos afterlarger combos, or else the smaller combos will override the larger combos if they are similar enough. Example:

如果检查不同数量的组合(如CtrlShiftAltEnterCtrlEnter),请将较小的组合放在较大的组合之后,否则如果它们足够相似,较小的组合将覆盖较大的组合。例子:

// 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: "This key combo keeps activating even though I'm not pressing the keys"

问题:“即使我没有按下按键,这个组合键也会一直激活”

When dealing with alerts or anything that takes focus from the main window, you might want to include map = []to reset the array after the condition is done. This is because some things, like alert(), take the focus away from the main window and cause the 'keyup' event to not trigger. For example:

在处理警报或从主窗口获取焦点的任何内容时,您可能希望包括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

Gotcha: Browser Defaults

陷阱:浏览器默认值

Here's an annoying thing I found, with the solution included:

这是我发现的一件烦人的事情,解决方案包括:

Problem: Since the browser usually has default actions on key combos (like CtrlDactivates the bookmark window, or CtrlShiftCactivates skynote on maxthon), you might also want to add return falseafter map = [], so users of your site won't get frustrated when the "Duplicate File" function, being put on CtrlD, bookmarks the page instead.

问题:由于浏览器通常对按键组合具有默认操作(例如CtrlD激活书签窗口,或CtrlShiftC在 maxthon 上激活 Skynote),您可能还想添加return falseafter map = [],这样您网站的用户就不会在出现“重复文件”时感到沮丧功能,被放在CtrlD,改为书签页面。

if(map[17] && map[68]){ // CTRL+D
    alert('The bookmark window didn\'t pop up!');
    map = {};
    return false;
}

Without return false, the Bookmark window wouldpop up, to the dismay of the user.

如果没有return false,书签窗口弹出,让用户感到沮丧。

The return statement (new)

返回语句(新)

Okay, so you don't always want to exit the function at that point. That's why the event.preventDefault()function is there. What it does is set an internal flag that tells the interpreter to notallow the browser to run its default action. After that, execution of the function continues (whereas returnwill immediately exit the function).

好的,所以你并不总是想在那个时候退出函数。这就是该event.preventDefault()功能存在的原因。它的作用是设置一个内部标志,告诉解释,以使浏览器运行的默认操作。之后,函数继续执行(而return将立即退出函数)。

Understand this distinction before you decide whether to use return falseor e.preventDefault()

在决定是否使用return falsee.preventDefault()

event.keyCodeis deprecated

event.keyCode已弃用

User SeanVieirapointed out in the comments that event.keyCodeis deprecated.

用户SeanVieiraevent.keyCode已弃用的评论中指出。

There, he gave an excellent alternative: event.key, which returns a string representation of the key being pressed, like "a"for A, or "Shift"for Shift.

在那里,他给出了一个很好的替代方案:event.key,它返回被按下的键的字符串表示,如"a"forA"Shift"for Shift

I went ahead and cooked up a toolfor examining said strings.

我继续编写了一个用于检查所述字符串的工具

element.oneventvs element.addEventListener

element.onevent对比 element.addEventListener

Handlers registered with addEventListenercan be stacked, and are called in the order of registration, while setting .oneventdirectly is rather aggressive and overrides anything you previously had.

注册的处理程序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
});

The .oneventproperty seems to override everything and the behavior of ev.preventDefault()and return false;can be rather unpredictable.

.onevent属性似乎覆盖了所有内容和行为,ev.preventDefault()并且return false;可能相当不可预测。

In either case, handlers registered via addEventlistenerseem to be easier to write and reason about.

在任何一种情况下,通过注册的处理程序addEventlistener似乎更容易编写和推理。

There is also attachEvent("onevent", callback)from Internet Explorer's non-standard implementation, but this is beyond deprecated and doesn't even pertain to JavaScript (it pertains to an esoteric language called JScript). It would be in your best interest to avoid polyglot code as much as possible.

还有attachEvent("onevent", callback)来自 Internet Explorer 的非标准实现,但这已经过时了,甚至与 JavaScript 无关(它与称为JScript的深奥语言有关)。尽可能避免多语言代码符合您的最大利益。

A helper class

辅助类

To address confusion/complaints, I've written a "class" that does this abstraction (pastebin link):

为了解决混淆/投诉,我编写了一个“类”来进行这种抽象(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();
}

This class doesn't do everything and it won't handle every conceivable use case. I'm not a library guy. But for general interactive use it should be fine.

这个类不会做所有事情,也不会处理所有可以想象的用例。我不是图书馆人。但是对于一般的交互式使用,它应该没问题。

To use this class, create an instance and point it to the element you want to associate keyboard input with:

要使用此类,请创建一个实例并将其指向要与键盘输入关联的元素:

var input_txt = Input(document.getElementById("txt"));

input_txt.watch("print_5", function(){
    txt.value += "FIVE ";
}, "Control", "5");

What this will do is attach a new input listener to the element with #txt(let's assume it's a textarea), and set a watchpoint for the key combo Ctrl+5. When both Ctrland 5are down, the callback function you passed in (in this case, a function that adds "FIVE "to the textarea) will be called. The callback is associated with the name print_5, so to remove it, you simply use:

这将做的是将一个新的输入侦听器附加到元素上#txt(假设它是一个文本区域),并为键组合设置一个观察点Ctrl+5。当Ctrl5都关闭时,将调用您传入的回调函数(在本例中为添加"FIVE "到 textarea的函数)。回调与 name 相关联print_5,因此要删除它,您只需使用:

input_txt.unwatch("print_5");

To detach input_txtfrom the txtelement:

input_txttxt元素分离:

input_txt.detach();

This way, garbage collection can pick up the object (input_txt), should it be thrown away, and you won't have an old zombie event listener left over.

这样,垃圾收集可以捡起对象 ( input_txt),如果它被扔掉,你就不会剩下一个旧的僵尸事件侦听器。

For thoroughness, here is a quick reference to the class's API, presented in C/Java style so you know what they return and what arguments they expect.

为全面起见,这里是对类 API 的快速参考,以 C/Java 风格呈现,因此您知道它们返回什么以及它们期望什么参数。

Boolean  key_down (String key);

Returns trueif keyis down, false otherwise.

Boolean  keys_down (String key1, String key2, ...);

Returns trueif all keys key1 .. keyNare down, false otherwise.

void     watch (String name, Function callback, String key1, String key2, ...);

Creates a "watchpoint" such that pressing all of keyNwill trigger the callback

void     unwatch (String name);

Removes said watchpoint via its name

void     clear (void);

Wipes the "keys down" cache. Equivalent to map = {}above

void     detach (void);

Detaches the ev_kdownand ev_kuplisteners from the parent element, making it possible to safely get rid of the instance

Boolean  key_down (String key);

true如果key关闭则返回,否则返回false。

Boolean  keys_down (String key1, String key2, ...);

true如果所有键key1 .. keyN都按下,则返回,否则返回false。

void     watch (String name, Function callback, String key1, String key2, ...);

创建一个“观察点”,以便按下所有的keyN将触发回调

void     unwatch (String name);

通过其名称删除所述观察点

void     clear (void);

擦除“keys down”缓存。相当于map = {}上面

void     detach (void);

ev_kdownev_kup侦听器与父元素分离,从而可以安全地摆脱实例

Update 2017-12-02In response to a request to publish this to github, I have created a gist.

更新 2017-12-02为了响应将其发布到 github 的请求,我创建了一个gist

Update 2018-07-21I've been playing with declarative style programming for a while, and this way is now my personal favorite: fiddle, pastebin

更新 2018-07-21我一直在玩声明式编程一段时间,现在这种方式是我个人的最爱:fiddlepastebin

Generally, it'll work with the cases you would realistically want (ctrl, alt, shift), but if you need to hit, say, a+wat the same time, it wouldn't be too difficult to "combine" the approaches into a multi-key-lookup.

通常,它适用于您实际想要的情况(ctrl、alt、shift),但是如果您需要同时点击a+w,那么将这些方法“组合”成一个多键查找。



I hope this thoroughly explained answermini-blog was helpful :)

我希望这个彻底解释的答案迷你博客有帮助:)

回答by Martijn

You should use the keydownevent to keep track of the keys pressed, andyou should use the keyupevent to keep track of when the keys are released.

您应该使用keydown事件来跟踪按下的键,并且应该使用keyup事件来跟踪何时释放键。

See this example: http://jsfiddle.net/vor0nwe/mkHsU/

请参阅此示例:http: //jsfiddle.net/vor0nwe/mkHsU/

(Update: I'm reproducing the code here, in case jsfiddle.net bails:) The HTML:

(更新:我在这里复制代码,以防 jsfiddle.net 保释 :) HTML:

<ul id="log">
    <li>List of keys:</li>
</ul>

...and the Javascript (using jQuery):

...和 ​​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');
});

In that example, I'm using an array to keep track of which keys are being pressed. In a real application, you might want to deleteeach element once their associated key has been released.

在那个例子中,我使用一个数组来跟踪按下了哪些键。在实际应用程序中,您可能希望在delete每个元素的关联键被释放后对其进行处理。

Note that while I've used jQuery to make things easy for myself in this example, the concept works just as well when working in 'raw' Javascript.

请注意,虽然我在本示例中使用 jQuery 使事情变得简单,但这个概念在使用“原始”Javascript 时同样有效。

回答by Eduardo La Hoz Miranda

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");

    } 

}

回答by Array

I used this way (had to check wherever is Shift + Ctrl pressed):

我使用这种方式(必须检查 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;
    }
});

回答by Reza Ramezanpour

for who needs complete example code. Right+Left added

谁需要完整的示例代码。右+左添加

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);

回答by AnonymousGuest

Make the keydown even call multiple functions, with each function checking for a specific key and responding appropriately.

使 keydown 甚至调用多个函数,每个函数检查一个特定的键并做出适当的响应。

document.keydown = function (key) {

    checkKey("x");
    checkKey("y");
};

回答by Michael Lester

If one of keys pressed is Alt / Crtl / Shift you can use this method:

如果按下的键之一是 Alt / Crtl / Shift 您可以使用此方法:

document.body.addEventListener('keydown', keysDown(actions) );

function actions() {
   // do stuff here
}

// simultaneous pressing Alt + R
function keysDown (cb) {
  return function (zEvent) {
    if (zEvent.altKey &&  zEvent.code === "KeyR" ) {
      return cb()
    }
  }
}

回答by Prosun Chakraborty

    $(document).ready(function () {
        // using ascii 17 for ctrl, 18 for alt and 83 for "S"
        // ctr+alt+S
        var map = { 17: false, 18: false, 83: false };
        $(document).keyup(function (e) {
            if (e.keyCode in map) {
                map[e.keyCode] = true;
                if (map[17] && map[18] && map[83]) {
                    // Write your own code here, what  you want to do
                    map[17] = false;
                    map[18] = false;
                    map[83] = false;
                }
            }
            else {
                // if u press any other key apart from that "map" will reset.
                map[17] = false;
                map[18] = false;
                map[83] = false;
            }
        });

    });

回答by Jakub Muda

This is not a universal method, but it's usefull in some cases. It's usefull for combinations like CTRL+ somethingor Shift+ somethingor CTRL+ Shift+ something, etc.

这不是通用方法,但在某些情况下很有用。它对于CTRL+somethingShift+somethingCTRL+ Shift+something等组合很有用。

Example: When you want to print a page using CTRL+ P, first key pressed is always CTRLfollowed by P. Same with CTRL+ S, CTRL+ Uand other combinations.

示例:当您想使用CTRL+打印页面时P,按下的第一个键总是CTRL后跟P。与CTRL+ SCTRL+U和其他组合相同。

document.addEventListener('keydown',function(e){

  //SHIFT + something
  if(e.shiftKey){
    switch(e.code){

      case 'KeyS':
        alert('Shift + S');
        break;

    }
  }

  //CTRL + SHIFT + something
  if(e.ctrlKey && e.shiftKey){
    switch(e.code){

      case 'KeyS':
        alert('CTRL + Shift + S');
        break;

    }
  }

});

回答by Anonymous

case 65: //A
jp = 1;
setTimeout("jp = 0;", 100);

if(pj > 0) {
ABFunction();
pj = 0;
}
break;

case 66: //B
pj = 1;
setTimeout("pj = 0;", 100);

if(jp > 0) {
ABFunction();
jp = 0;
}
break;

Not the best way, I know.

不是最好的方法,我知道。