I'm trying to develop a JavaScript game engine and I've came across this problem:
- When I press SPACE the character jumps.
- When I press → the character moves right.
The problem is that when I'm pressing right and then press space, the character jumps and then stops moving.
I use the keydown
function to get the key pressed. How can I check if there are multiple keys pressed at once?
Not the best way, I know.
for who needs complete example code. Right+Left added
Make the keydown even call multiple functions, with each function checking for a specific key and responding appropriately.
Multiple keystroke detection is easy if you understand the concept
The way I do it is like this:
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 A and B, each fires a
keydown
event that setsmap[e.keyCode]
to the value ofe.type == keydown
, which evaluates to either true or false. Now bothmap[65]
andmap[66]
are set totrue
. When you let go ofA
, thekeyup
event fires, causing the same logic to determine the opposite result formap[65]
(A), which is now false, but sincemap[66]
(B) is still "down" (it hasn't triggered a keyup event), it remains true.The
map
array, through both events, looks like this: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
.Note: You can easily grab an element by its
id
attribute.This creates an html element that can be easily referenced in javascript with
element
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.Just make sure the script tag comes after the body of the HTML. Optimization tip: Most big-name websites put the script tag after the 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.
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: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:
Usage:
Is this better?
(end of edit)
This example checks for CtrlShiftA, CtrlShiftB, and CtrlShiftC
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.You should also put the key codes in the same order as the documentation (
CTRL+ENTER => map[17] && map[13]
, NOTmap[13] && map[17]
). This way you won't ever get confused when you need to go back and edit the code.A gotcha with if-else chains
If checking for combos of differing amounts (like CtrlShiftAltEnter and CtrlEnter), put smaller combos after larger combos, or else the smaller combos will override the larger combos if they are similar enough. Example:
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, likealert()
, take the focus away from the main window and cause the 'keyup' event to not trigger. For example: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 CtrlD activates the bookmark window, or CtrlShiftC activates skynote on maxthon), you might also want to add
return false
aftermap = []
, so users of your site won't get frustrated when the "Duplicate File" function, being put on CtrlD, bookmarks the page instead.Without
return false
, the Bookmark window would pop up, to the dismay of the user.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 not allow the browser to run its default action. After that, execution of the function continues (whereasreturn
will immediately exit the function).Understand this distinction before you decide whether to use
return false
ore.preventDefault()
event.keyCode
is deprecatedUser SeanVieira pointed out in the comments that
event.keyCode
is deprecated.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.I went ahead and cooked up a tool for examining said strings.
element.onevent
vselement.addEventListener
Handlers registered with
addEventListener
can be stacked, and are called in the order of registration, while setting.onevent
directly is rather aggressive and overrides anything you previously had.The
.onevent
property seems to override everything and the behavior ofev.preventDefault()
andreturn false;
can be rather unpredictable.In either case, handlers registered via
addEventlistener
seem to be easier to write and reason about.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.A helper class
To address confusion/complaints, I've written a "class" that does this abstraction (pastebin link):
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:
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 comboCtrl+5
. When bothCtrl
and5
are 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 nameprint_5
, so to remove it, you simply use:To detach
input_txt
from thetxt
element: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.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.
Update 2017-12-02 In response to a request to publish this to github, I have created a gist.
Update 2018-07-21 I've been playing with declarative style programming for a while, and this way is now my personal favorite: fiddle, pastebin
Generally, it'll work with the cases you would realistically want (ctrl, alt, shift), but if you need to hit, say,
a+w
at the same time, it wouldn't be too difficult to "combine" the approaches into a multi-key-lookup.I hope this
thoroughly explained answermini-blog was helpful :)I'd try adding a
keypress
Event
handler uponkeydown
. E.g:This is just meant to illustrate a pattern; I won't go into detail here (especially not into browser specific level2+
Event
registration).Post back please whether this helps or not.