First, I would like to thank all for your help and replies.
I'm working on a project that required users to press a key and hold it, it will trigger an action.
When the user presses another key ( still holing the first key), it will trigger another action.
However, I'm stuck getting JavaScript to recognize two keys being pressed at the same time.
var down = false;
var keys;
document.addEventListener("keydown", function (e) {
if (down) {return;}
down = true;
keys = (keys || []);
keys[e.keyCode]=true;
if (keys[87]){
console.log("1");
}
else if (keys[83]){
console.log("2");
}
else if (keys[83] && keys[87]){
console.log("sucessfull");
}
} , false);
document.addEventListener("keyup", function (e) {
down = false;
keys[e.keyCode]=false;
stop();
}, false);
<button id="up" onmousedown="Drive(1)" onmouseup="Drive(2)">UP </button>
Removed the 'down' variable checks
Removed the mouse button element thing - didn't seem relevant to your problem?
Removed the extra HTML body
Got rid of the else branching as it would be satisfied (by 87 or 83) before ever getting to the && condition
var keys;
document.addEventListener("keydown", function (e) {
keys = (keys || []);
keys[e.keyCode]=true;
if (keys[87]){
console.log("1");
}
if (keys[83]){
console.log("2");
}
if (keys[83] && keys[87]){
console.log("sucessfull");
}
} , false);
document.addEventListener("keyup", function (e) {
keys[e.keyCode]=false;
stop();
}, false);
Related
I have code for 3 different task which I want to execute by clicking and pressing a key, so there will be 3 different combination of clicking and pressing. For example-
window.addEventListener("keydown", function(e){
if(e.keyCode === 16) {console.log('Yap! Shift works...');}
if(e.keyCode === 17) {console.log('Yap! Ctrl works...');
document.addEventListener('click',function (event) {
console.log(event.target.className);
}, false);
}
},false);
Now, when I press click shift key, I get related output, when I click Ctrl key and then click, I get the class name of the object I click on.
But the problem is, the output keeps coming as much I hold the key!! I want to execute the part of my code for once, and exactly when the key is pressed and a clicked is occurred.
How can I do that?
In general, how can I execute 3 part of code for three different tasks by clicking and pressing efficiently?
Adding an event handler while handing an event, is often the wrong way to solve a problem. Imagine how you will accumulate adding handlers... in your case there will eventually be many bindings to the same click handler.
It is better to bind the handlers you need immediately, and then work with keeping state on what exactly needs to happen while handling the event.
In these key handlers (keydown, keyup), keep track of whether the Shift/Control keys are depressed or not.
Also, use e.key as e.keyCode is deprecated.
Here is how that could work:
let keys = {
"Shift": false,
"Control": false
};
function keyToggle(e) {
if (!(e.key in keys)) return; // not ctrl or shift
let isKeyDown = e.type === "keydown";
if (isKeyDown === keys[e.key]) return; // key position did not change
keys[e.key] = isKeyDown;
console.log(e.key + (isKeyDown ? " pressed" : " released"));
}
document.addEventListener("keydown", keyToggle, false);
document.addEventListener("keyup", keyToggle, false);
document.addEventListener('click', function (e) {
if (keys["Control"]) console.log(event.target.className);
}, false);
<main class="main">Main</main>
<aside class="aside">Aside</aside>
As you addEventListener you can also removeEventListener.
For that you need a reference to your event handler, so you cannot use anonymous functions, but named functions or functions stored in a variable.
Edit
Here is an example of using CTRL+click:
// CTRL + CLICK implementation
let hasCtrl = false;
// Store the handler in a constant or variable
const handleClick = function(event) {
console.log(event.target.className);
}
// Use named function
function handleKeyDown(e) {
if (e.keyCode === 16) {
console.log('Yap! Shift works...');
}
}
const setCtrlInactive = (e) => {
if (!hasCtrl && e.keyCode === 17) {
console.log('Nope! Ctrl does not work...');
document.removeEventListener('click', handleClick);
hasCtrl = true;
}
}
const setCtrlActive = (e) => {
if (hasCtrl && e.keyCode === 17) {
console.log('Yap! Ctrl works...');
document.addEventListener('click', handleClick);
hasCtrl = false;
}
}
document.addEventListener("keyup", setCtrlInactive);
document.addEventListener("keydown", setCtrlActive);
document.addEventListener("keydown", handleKeyDown);
<main class="main">Main</main>
<aside class="aside">Aside</aside>
Well you can easily create an variable to lock it:
var locked = false;
window.addEventListener("keydown", function(e){
if(e.keyCode === 16 && !locked) {console.log('Yap! Shift works...'); locked =
true;}
if(e.keyCode === 17 && !locked ) {console.log('Yap! Ctrl works...');
locked = true;
}
},false);
document.addEventListener('click',function (event) {
if(locked){
// do something
console.log(event.target.className);
}
}, false);
window.addEventListener("keyup", function(){
locked = false;
}
That's because you called addEventListener('click') in the keydown event handler.
let ctrl = false;
window.addEventListener("keydown", function(e) {
if (e.keyCode === 16) {
console.log('Yap! Shift works...');
}
if (e.keyCode === 17 && ctrl === false) {
console.log('Yap! Ctrl works...');
ctrl = true;
}
});
window.addEventListener("keyup", function(e) {
if (e.keyCode === 17) {
ctrl = false;
}
});
document.addEventListener('click', function(event) {
if (ctrl) {
console.log(event.target.className);
}
}, false);
Instead, you should use keyup event and flag variable.
I'm making a simple JavaScript game (Space Invaders style) (Top-down space shooter) and I'm trying to make my character shoot a bullet per each 'space' key press. How can I do that?
I have tried multiple approaches, setting a flag, using onkeypress instead of keydown, Google searches (have also encountered this similar question yet it didn't help: Javascript onkeydown event fire only once?)
Below is an example of one solution I have tried.
document.onkeydown = function(e)
{
if(e.keyCode == 32 && space == false)
{
space = true;
}
}
document.onkeyup = function(e)
{
if(e.keyCode == 32) space = false;
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp)
{ if(space === true)
{
p.shoot();
}
window.requestAnimationFrame(gameLoop);
}
Expected results: Key being fired only once.
Actual results: Key is being fired multiple times.
There's really 3 states to a bullet - and, taking into consideration that in classic Space Invaders the player can only have one bullet in flight at a time, this makes things relatively simple
The bullet can be
NONE - doesn't exist
FIRED - i.e. need to create one
EXISTS - it's in flight
The code to handle it is relatively simple too
var BulletState = {
NONE: 0,
FIRED: 1,
EXISTS: 2
};
var bullet = BulletState.NONE;
document.onkeydown = function(e) {
if (e.keyCode == 32 && bullet === BulletState.NONE) {
bullet = BulletState.FIRED;
}
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
if (bullet === BulletState.FIRED) {
bullet = BulletState.EXISTS;
p.shoot(); // p.shoot needs to set bullet = BulletState.NONE when the bullet expires
}
window.requestAnimationFrame(gameLoop);
}
document.onkeyup = function(e)
{
// not required at all
}
edit: To fire a bullet every press of spacebar (allowing multiple bullets)
var space = false;
var fireBullet = 0;
document.onkeydown = function(e) {
if (e.keyCode == 32 && !e.repeat && !space) {
fireBullet++;
space = true;
}
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
while (fireBullet) {
p.shoot();
fireBullet--;
}
window.requestAnimationFrame(gameLoop);
}
document.onkeyup = function(e)
{
space = false;
}
I use fireBullet++ and fireBullet-- because I guess it's plausible that someone could press and release and press the spacebar within 16ms (single frame) :p
You could also do
function gameLoop(timeStamp) {
if (fireBullet) {
p.shoot();
fireBullet--;
}
window.requestAnimationFrame(gameLoop);
}
that way you still handle multiple bullets OK, but only fire off one per frame - really depends on how you want to handle someone with a very fast trigger finger :p
As #JaromandaX suggested, space = false; after p.shoot(); will give you the desired result:
Reproducing the issue:
var space = false;
var element = document.querySelector('input');
element.onkeydown = function(e) {
if (!space && e.keyCode == 32) {
space = true;
console.log('event fired');
}
};
element.onkeyup = function(e) {
if (e.keyCode == 32)
space = false;
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
if (space == true) {
console.log('shoot');
}
window.requestAnimationFrame(gameLoop);
}
<input />
EDIT: Added another isShot variable to keep track of shot fired and space key is down in requestAnimationFrame event:
var space = false;
var element = document.querySelector('input');
var isShot = false;
element.onkeydown = function(e) {
if (!space && e.keyCode == 32) {
space = true;
console.log('event fired');
}
};
element.onkeyup = function(e) {
if (e.keyCode == 32) {
space = isShot = false;
}
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
if (space && !isShot) {
console.log('shoot');
isShot = true;
}
window.requestAnimationFrame(gameLoop);
}
<input />
Here you have two event, on each key press three are three even that get's fired
1. keydown (when user press the key but not yet release the key)
2. keypress (its fired after the keydown event)
3. keyup (When user release the key)
If you want to fire only once for each keypress
implement on of those if implement all of the three all three methods will fired.
Additionally: for safety you can write e.preventDefault().
I'm trying to execute some code when multiple buttons are pushed, I'm trying this as an example but it's not working :
<script>
var map = {82: false, 84: false};
function keydown(e) {
if (e.keyCode in map) {
map[e.keyCode] = true;
if (map[82] && map[84]) {
alert(" all pressed ");
}
}
}
function keyup(e)
{
if (e.keyCode in map) {
map[e.keyCode] = false;
}
}
window.addEventListener('keyup', keyup);
window.addEventListener('keydown', keydown);
</script>
I get the alert even when only one button is pushed, ( i got it when both are pushed too )
What am i doing wrong please ?
You don't seem to be attaching your keyup handler, so once a key has been pressed it was forever marked as true in your map
window.addEventListener('keyup', keyup);
Try this:
var map = [];
onkeydown = onkeyup = function(e){
e = e || event; // to deal with IE
map[e.keyCode] = e.type == 'keydown';
if(map[82] && map[84]){
alert('all pressed');
}
}
This was also already answered here:
JavaScript multiple keys pressed at once
Please make sure you check to avoid posting a duplicate question.
I think I found the problem, the program doesn't execute the keyup fonction because of the alert, map[82] and map[84] are always true then.
I am developing a game where you must press the left and right arrow keys alternatively to make the character move, the faster you do it, the quicker he runs. I have however ran into a problem whereby the key is being "held down" in a sense so no matter how quick you press the key it still manages to execute it multiple times.
So I am looking for a way to make the key only be pressed once per press rather than updating if you hold down the key.
here is the code for my key capturing and what it executes at the moment (Which is just an update of points and an update of the image used for the character.
KEY_CODES = {
37: 'left',
39: 'right',
40: 'down',
}
KEY_STATUS = {};
for (code in KEY_CODES) {
KEY_STATUS[KEY_CODES[code]] = false;
}
document.onkeydown = function (e) {
var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
if (KEY_CODES[keyCode]) {
e.preventDefault();
KEY_STATUS[KEY_CODES[keyCode]] = true;
}
}
document.onkeyup = function (e) {
var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
if (KEY_CODES[keyCode]) {
e.preventDefault();
KEY_STATUS[KEY_CODES[keyCode]] = false;
}
}
function move() {
movecounter++;
// Determine if the action is move action
if (KEY_STATUS.left || KEY_STATUS.right ||
KEY_STATUS.down || KEY_STATUS.up) {
// Redraw the canavs background images ready for the new ones to be placed ontop.
paintCanvas();
// to have diagonal movement.
if (KEY_STATUS.left) {
ctx.drawImage(imageStore.snowWalk, playerPosW, playerPosH);
snowStand = true;
score += 10;
} else if (KEY_STATUS.right) {
ctx.drawImage(imageStore.snowWalk, playerPosW, playerPosH);
score += 10;
} else if (KEY_STATUS.down) {
ctx.drawImage(imageStore.snowCrouch, playerPosW, playerPosH);
snowStand = false;
}
}
};
Hope you understand the problem I am facing, I have tried to explain it as best as I can here.
Thanks!
A simple solution would be to have a variable outside of the scope of the event listener that tells you if you're pressing the key for the first time:
var pressed = false;
Then, in the listener, toggle the state of that variable when the key is pressed:
.keydown(function() {
if (pressed) return;
pressed = true;
}
.keyup(function() {
pressed = false;
}
so right now I'm using a function that will set a value to true if one key being pressed, another being pressed regardless of whether or not the first one is still depressed.
function doc_keyUp1(e) {
if (e.keyCode == 37){
lPunch = true
}
}
function doc_keyUp2(e) {
if (e.keyCode == 39){
rPunch = true
}
}
document.addEventListener('keyup', doc_keyUp1, false)
document.addEventListener('keyup', doc_keyUp2, false)
The thing is, I want to be able to have it make sure that if the second key is being pressed, that the first one must still be down, so that someone can't just press one then the other quickly and make it seem as if they were both pressed down at the same time.
Any ideas?
Assuming you have some kind of "game loop" something like the following works (or perhaps I should say "should work", in that I haven't coded something like this for a long time and so haven't tested it with current browsers - definitely used to work):
var keyPressed = {};
document.addEventListener('keydown', function(e) {
keyPressed[e.keyCode] = true;
}, false);
document.addEventListener('keyup', function(e) {
keyPressed[e.keyCode] = false;
}, false);
function gameLoop() {
if (keyPressed["39"] && keyPressed["37"]) {
// do something (update player object state, whatever)
}
// etc
// update display here
setTimeout(gameLoop, 5);
}
gameLoop();
I'd suggest you use an Array to hold key states.
var keyStates = [ ];
document.addEventListener('keydown', function(e) {
keyStates.push( e.keyCode );
}, false);
document.addEventListener('keyup', function(e) {
var pos = null;
if( (pos = keyStates.indexOf( e.keyCode )) > -1 )
keyStates.splice( pos, 1 );
}, false);
So with that, you can always check that array for keys currently beeing pushed.
var currentKeyCodes=new Object();
function keyDown(e) {
currentKeyCodes['x'+e.keyCode]=true;
}
function keyUp(e) {
//Real check here
if ((e.keyCode==39) && currentKeyCodes['x37']) {
do_whatever_you_want();
}
//Housekeeping
var s='x'+e.keyCode;
if (currentKeyCodes[s]) currentKeyCodes[2]=false;
}