Reputation: 15725
I have this Plunkr,
This contains a div
, on which I bind a keydown
event. On pressing right or left arrow, the div should start moving.
This works, in all browsers, but when the key is pressed and hold, the first keydown event is fired immediately (div once moves), and it waits for a time gap, and then it continues to move.
So this means the keydown
event is once fired, then the browser waits to detect if there is a subsequent keyUp
event as well, then after a short time (when there is no keyup), it continues to fires keydown
events.
(to see the problem, focus on window, press right arrow and hold, div should move once 5px, then wait, and then again continue to move)
Question: Is there a way around, so I can just keep the key pressed, and div should immediately start moving, without waiting to detect subsequent keyup (once)?
$(function() {
$(window).keydown(function(e) {
console.log(e.keyCode)
if (e.keyCode == 39)
move(5, 'left', $('.mover'))
else if (e.keyCode == 37)
move(-5, 'left', $('.mover'))
})
})
function move(offset, direction, target) {
console.log($(target))
$(target).css(direction, (parseInt($(target).css(direction)) + offset) + 'px')
}
.mover {
height: 50px;
width: 50px;
display: inline-block;
background: black;
position: absolute;
left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class='mover'></div>
Upvotes: 3
Views: 10053
Reputation: 11007
pure javascript
<input id="some" type="text">
var input=document.getElementById("some");
input.addEventListener("keydown",function(e){
if (e.which == 40) {
var stop = setInterval(function(){
console.log("ss:");
}, 60);
window.addEventListener("keyup", function(){
//stop the loop
clearInterval(stop);
//and remove the keyup listener
window.removeEventListener("keyup", arguments.callee);
})
}
})
Upvotes: 0
Reputation: 923
For those that might interest, here is another approach to handle bi-directional movement. Ex: up
+ right
keys = "North East" direction :
const FPS = 60;
const STEP = 5;
const UP_KEY = 90; // Z
const DOWN_KEY = 83; // S
const LEFT_KEY = 81; // Q
const RIGHT_KEY = 68; // D
const pressedKeys = [];
let movingTimeout = null;
function handleKeyDown(e) {
pressedKeys[e.keyCode] = true;
switch(e.keyCode) {
case DOWN_KEY:
case LEFT_KEY:
case RIGHT_KEY:
case UP_KEY:
e.preventDefault();
startMoving();
}
}
function handleKeyUp(e) {
pressedKeys[e.keyCode] = false;
const shouldStop = !pressedKeys[UP_KEY]
&& !pressedKeys[DOWN_KEY]
&& !pressedKeys[LEFT_KEY]
&& !pressedKeys[RIGHT_KEY]
;
if(shouldStop) {
stopMoving();
}
}
function startMoving() {
if(!movingTimeout){
loop();
}
}
function stopMoving() {
clearTimeout(movingTimeout);
movingTimeout = null;
}
function loop() {
const x = pressedKeys[RIGHT_KEY] ? STEP
: pressedKeys[LEFT_KEY] ? -STEP : 0;
const y = pressedKeys[DOWN_KEY] ? STEP
: pressedKeys[UP_KEY] ? -STEP : 0;
move(x, y);
movingTimeout = setTimeout(loop, 1000 / FPS);
}
function move(x, y) {
// Implement you own logic here for positioning / handling collisions
// Ex: store.dispatch({ type: "MOVE_PLAYER", x, y });
console.log(`Moving ${x} ${y} !`);
}
window.addEventListener('keydown', e => handleKeyDown(e));
window.addEventListener('keyup', e => handleKeyUp(e));
Hope this helps.
Cheers !
Upvotes: 0
Reputation: 136
I would suggest something on a timeout, like this
http://codepen.io/kevrowe/pen/qEgGVO
$(function() {
var direction,
movingTimeout = -1;
$(window).on('keydown', function(e) {
if (e.keyCode == 39) {
direction = 'right';
} else if (e.keyCode == 37) {
direction = 'left';
}
startMoving(direction);
});
function stopMoving() {
clearTimeout(movingTimeout);
movingTimeout = -1;
}
function startMoving(direction) {
if (movingTimeout === -1) {
loop(direction);
}
}
function loop(direction) {
move(direction === 'left' ? -5 : 5, $('.mover'));
movingTimeout = setTimeout(loop, 10, direction);
}
function move(offset, $target) {
$target.css('left', (parseInt($target.css('left')) + offset) + 'px')
}
$(window).on('keyup', function(e) {
stopMoving();
});
})
Upvotes: 6
Reputation: 6444
One solution would be to just continuously run the move function in a loop until keyup happens
if (e.keyCode == 39){
var stop = setInterval(function(){
move(5, 'left', $('.mover'))
}, 25);
window.on("keyup", function(){
//stop the loop
clearInterval(stop);
//and remove the keyup listener
window.off("keyup", arguments.callee);
})
} else if //etc...
Upvotes: 1
Reputation: 3620
When keydown event occurs on the key you want to track, then store this information in a mapping or a flag somewhere.
When keyup event occurs, then clear the flag for the given key.
Then, on a timer, you could poll the state of the key mapping, and move the object for whatever key direction is being pressed.
Possibly there is a better solution then polling, but I don't know of a way to test if a key is down other than polling.
On keyup, would also need to check if needed to interrupt moving the object.
Upvotes: 2