Prevent button from continuously firing while being held - javascript

I am trying to prevent specific buttons from continuously firing but have had no luck with any of my methods. I've tried several suggestions/answers from other stackoverflow posts but none have worked. I currently have a controller and for my left/right buttons I don't care if they repeat but for my jump button I do not want to be able to hold the button and have the character keep jumping. The closest I got to making this work was by using a Map and setting the button to it but unfortunately on keyup the character would also jump. Here is my current code:
class Controller {
constructor() {
this.left = false;
this.up = false;
this.right = false;
this.down = false;
this.pressed = {};
let keyDown = (e) => {
if (e.code == 'ArrowLeft') this.left = true;
if (e.code == 'ArrowRight') this.right = true;
if (e.code == 'ArrowDown') this.down = true;
if (e.code === 'ArrowUp') {
if (this.pressed[e.code] === false) { return }
else {
this.pressed[e.code] = false;
this.up = true;
}
}
}
let keyUp = (e) => {
if (e.code == 'ArrowLeft') this.left = false;
if (e.code == 'ArrowRight') this.right = false;
if (e.code == 'ArrowDown') this.down = false;
if (e.code === 'ArrowUp') {
this.pressed[e.code] = true;
this.up = false;
}
}
window.addEventListener('keydown', keyDown);
window.addEventListener('keyup', keyUp);
}
}
let controller = new Controller();
and some alternates that I've tried also
this.pressed = false;
let keyDown = (e) => {
if (e.code == 'ArrowLeft') this.left = true;
if (e.code == 'ArrowRight') this.right = true;
if (e.code == 'ArrowDown') this.down = true;
console.log(controller.pressed)
if (e.code === 'ArrowUp') {
if (!this.pressed) { return }
this.pressed = false;
this.up = true;
}
}
let keyUp = (e) => {
if (e.code == 'ArrowLeft') this.left = false;
if (e.code == 'ArrowRight') this.right = false;
if (e.code == 'ArrowDown') this.down = false;
if (e.code === 'ArrowUp') {
this.pressed = true;
this.up = false;
}
}
this.pressed = false;
let keyDown = (e) => {
if (e.code == 'ArrowLeft') this.left = true;
if (e.code == 'ArrowRight') this.right = true;
if (e.code == 'ArrowDown') this.down = true;
if (!this.pressed) {
this.pressed = true;
if (e.code == 'ArrowUp') {
this.up = true;
}
}
}
let keyUp = (e) => {
if (e.code == 'ArrowLeft') this.left = false;
if (e.code == 'ArrowRight') this.right = false;
if (e.code == 'ArrowDown') this.down = false;
if (e.code == 'ArrowUp') {
this.pressed = false;
this.up = false;
}
}
Logically I feel as though these should work but they don't. And in case it matters my character jumps based on this
if (controller.up && !this.jumping) {this.vy -= this.speed * 60; this.jumping = true;}
I've probably tried doing this at least 10 different ways and the closest I got was my character jumped twice before stopping (while holding arrowup).
EDIT: this gives 2 jumps then stops. Can anyone explain why he jumps twice before stopping?
this.keySet = new Set();
let keyDown = (e) => {
if (e.code == 'ArrowUp') {
if (this.keySet.has(e.code)) { this.up = false }
else { this.keySet.add(e.code); this.up = 'keydown' === e.type }
}
}
let keyUp = (e) => {
if (e.code == 'ArrowUp') { this.keySet.delete(e.code); this.up = false }
}

You can throttle the function
The way throttling works is when you call a function, it will execute the function like normal, but on subsequent calls it won't run again until enough time has passed since the last attempted execution.
Lodash has an implementation, but you can find versions of this all over the internet: https://lodash.com/docs/4.17.15#throttle

Related

How to use event.code and event.button in if, else condition properly?

my goal is to build playing drums using vanilla Javascript, whole code worked perfectly when I was using only the keydown event, unfortunately, my drums have to play also on mouse 'click'.
When I added "|| event.button == aKey == 0" this part to my condition, it still plays, but only one sound on each key. So I assumed that there is something wrong with my condition.
And also if it would be possible to somehow make my code less repeatable, I would appreciate it.
const aKey = document.getElementById("A-key");
const sKey = document.getElementById("S-key");
const dKey = document.getElementById("D-key");
const fKey = document.getElementById("F-key");
const gKey = document.getElementById("G-key");
const hKey = document.getElementById("H-key");
const jKey = document.getElementById("J-key");
const kKey = document.getElementById("K-key");
const lKey = document.getElementById("L-key");
const playFunction = (event) => {
if (event.code == "KeyA" || event.button == aKey == 0) {
//
let audioA = document.createElement("AUDIO");
if (audioA.canPlayType("audio/wav")) {
audioA.setAttribute("src","sounds/boom.wav");
}
audioA.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioA);
} else if (event.code == "KeyS" || event.button == sKey == 0 ) {
let audioS= document.createElement("AUDIO");
if (audioS.canPlayType("audio/wav")) {
audioS.setAttribute("src","sounds/clap.wav");
}
audioS.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioS);
} else if (event.code == "KeyD" || event.button == dKey == 0) {
let audioD = document.createElement("AUDIO");
if (audioD.canPlayType("audio/wav")) {
audioD.setAttribute("src","sounds/hihat.wav");
}
audioD.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioD);
} else if (event.code == "KeyF" || event.button == fKey == 0) {
let audioF = document.createElement("AUDIO");
if (audioF.canPlayType("audio/wav")) {
audioF.setAttribute("src","sounds/kick.wav");
}
audioF.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioF);
} else if (event.code == "KeyG" || event.button == gKey == 0) {
let audioG = document.createElement("AUDIO");
if (audioG.canPlayType("audio/wav")) {
audioG.setAttribute("src","sounds/openhat.wav");
}
audioG.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioG);
} else if (event.code == "KeyH" || event.button == hKey == 0) {
let audioH = document.createElement("AUDIO");
if (audioH.canPlayType("audio/wav")) {
audioH.setAttribute("src","sounds/ride.wav");
}
audioH.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioH);
} else if (event.code == "KeyJ" || event.button == jKey == 0) {
let audioJ = document.createElement("AUDIO");
if (audioJ.canPlayType("audio/wav")) {
audioJ.setAttribute("src","sounds/snare.wav");
}
audioJ.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioJ);
} else if (event.code == "KeyK" || event.button == kKey == 0) {
let audioK = document.createElement("AUDIO");
if (audioK.canPlayType("audio/wav")) {
audioK.setAttribute("src","sounds/tink.wav");
}
audioK.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioK);
} else if (event.code == "KeyL" || event.button == lKey == 0) {
let audioL = document.createElement("AUDIO");
if (audioL.canPlayType("audio/wav")) {
audioL.setAttribute("src","sounds/tom.wav");
}
audioL.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioL);
} else {
console.log("Try to use specified keys.")
}
};
window.addEventListener('keydown', playFunction, false);
window.addEventListener('click', playFunction, false);
event.button == kKey == 0 doesn't make much sense. This evaluates like (foo == bar) == baz, that is, the first condition is tested, then the result of that (true or false) is checked against the other condition. If you want to test multiple conditions, use || or && between every condition. Also, always use === to avoid surprising conversion behavior.
As for the refactor request, whenever you have a bunch of branches that only differ based on parameterizable values, like the button text or the sound, use a data structure such as an array or object and index/key into it.
Specifically, all you need here are key -> audio or audio URL pairs, so an object seems suitable.
Once you have such a structure, then you can access the power of loops to DRY out your code.
I don't have your audio files or markup, so you can try to adapt this proof of concept to your project:
const baseURL = `https://upload.wikimedia.org/wikipedia/commons/`;
const sounds = {
a: `7/7c/Bombo_Leg%C3%BCero_Grave.ogg`,
s: `7/7f/Bombo_Ac%C3%BAstico.ogg`,
d: `3/35/Bongo_Agudo.ogg`,
f: `4/44/Bongo_Grave.ogg`,
g: `b/b4/Sting.ogg`,
// ... add more key-sound URL pairs ...
};
Object.keys(sounds).forEach(key => {
const btn = document.createElement("button");
document.querySelector("#drum-pad").appendChild(btn);
const sound = new Audio(baseURL + sounds[key]);
sounds[key] = sound;
btn.textContent = key;
btn.addEventListener("click", e => {
sound.currentTime = 0;
sound.play();
});
});
document.addEventListener("keydown", e => {
if (sounds[e.key]) {
sounds[e.key].currentTime = 0;
sounds[e.key].play();
}
});
<div id="drum-pad"></div>

Keyboard Inputs [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
I'm coding a project and I've got a problem when I try getting keyboard input in a dedicated class.
So this is my Controller class :
Controller = function() {
// Initialisation clavier
document.onkeydown = this.onKeyDown;
document.onkeyup = this.onKeyUp;
// Touches
this.left = false;
this.up = false;
this.down = false;
this.right = false;
}
// Touche appuyée
Controller.prototype.onKeyDown = function(event) {
if (event.keyCode == 37) {
this.left = true;
}
else if (event.keyCode == 38) {
this.up = true;
}
else if (event.keyCode == 39) {
this.down = true;
}
else if (event.keyCode == 40) {
this.right = true;
}
}
// Touche relâchée
Controller.prototype.onKeyUp = function(event) {
if (event.keyCode == 37) {
this.left = false;
}
else if (event.keyCode == 38) {
this.up = false;
}
else if (event.keyCode == 39) {
this.down = false;
}
else if (event.keyCode == 40) {
this.right = false;
}
}
... with which I create an occurence during initialization. But when I try to get boolean state in an other class :
// Déplacement
Player.prototype.move = function() {
if (controler.left) {
this.posHorizontal -= this.speed;
}
}
this isn't working ! When I display the state in the controller class it return 'true' but not in another classes. I'v got no error but only a 'false' displayed (i've tried with console.log but no way).
Thanks for help !
When the browser calls onKeyDown and onKeyUp methods, after the events happen, the this keyword inside the functions points to the document not to the Controller instance.
change the lines
document.onkeydown = this.onKeyDown;
document.onkeyup = this.onKeyUp;
to
document.onkeydown = this.onKeyDown.bind(this);
document.onkeyup = this.onKeyUp.bind(this);
and it should fix the issue.

JS : Event on ScrollDown and ScrollUp

I currently have a animated sprite that walks when you press the left and right keys on the keyboard. What I would like to do is to make the sprite walk-right when you scroll down and walk left when you scroll up. I would also like to make the sprite stop walking when the user stops scrolling. Thanks in advance!
I tried using the $(window).scroll function with variables of current and lastscroll positions, but it didn't work.
function walk(e) {
var keyCode = e.keyCode;
if (keyCode === 39) {
key.right = true;
} else if (keyCode === 37) {
key.left = true;
}
if (key.right === true) {
trans += 0;
translate();
sprite.classList.remove('left');
sprite.classList.add('right');
sprite.classList.add('walk-right');
} else if (key.left === true) {
trans -= 0;
translate();
sprite.classList.remove('right');
sprite.classList.add('left');
sprite.classList.add('walk-left');
}
}
function stop(e) {
var keyCode = e.keyCode;
if (keyCode === 39) {
key.right = false;
} else if (keyCode === 37) {
key.left = false;
}
if (key.right === false) {
sprite.classList.remove('walk-right');
} if (key.left === false) {
sprite.classList.remove('walk-left');
}
}
Update:
Here's a better version; I merged the keyboard and scroll code into the same event for you:
if (document.addEventListener) {
// IE9, Chrome, Safari, Opera
document.addEventListener("mousewheel", walk, false);
// Firefox
document.addEventListener("DOMMouseScroll", walk, false);
}else{
// IE 6/7/8
document.attachEvent("onmousewheel", walk);
}
function walk(e) {
var e = window.event || e; // old IE support
if (e.keyCode) {
//keyboard input
if (e.keyCode === 39) {
key.right = true;
} else if (keyCode === 37) {
key.left = true;
}
}else{
//scroll input
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
if (delta == 1) {
//walk right
key.right = true;
key.left = false;
}else if (delta == -1) {
//walk left
key.left = true;
key.right = false;
}else{
//stop
key.left = false;
key.right = false;
sprite.classList.remove('walk-right');
sprite.classList.remove('walk-left');
}
}
if (key.right === true) {
trans += 0;
translate();
sprite.classList.remove('left');
sprite.classList.add('right');
sprite.classList.add('walk-right');
} else if (key.left === true) {
trans -= 0;
translate();
sprite.classList.remove('right');
sprite.classList.add('left');
sprite.classList.add('walk-left');
}
}
Previous answer:
Here you go:
if (document.addEventListener) {
// IE9, Chrome, Safari, Opera
document.addEventListener("mousewheel", scroll, false);
// Firefox
document.addEventListener("DOMMouseScroll", scroll, false);
}else{
// IE 6/7/8
document.attachEvent("onmousewheel", scroll);
}
function scroll(e) {
var e = window.event || e; // old IE support
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
if (delta == 1) {
//walk right
key.right = true;
key.left = false;
}else if (delta == -1) {
//walk left
key.left = true;
key.right = false;
}else{
//stop
key.left = false;
key.right = false;
sprite.classList.remove('walk-right');
sprite.classList.remove('walk-left');
}
if (key.right === true) {
trans += 0;
translate();
sprite.classList.remove('left');
sprite.classList.add('right');
sprite.classList.add('walk-right');
} else if (key.left === true) {
trans -= 0;
translate();
sprite.classList.remove('right');
sprite.classList.add('left');
sprite.classList.add('walk-left');
}
}
Use document.addEventListener instead:
var currentY = 0;
document.addEventListener('scroll', function(e){
// some logic
key.right = false;
key.left = false;
currentY < window.pageYOffset ? key.right = true : key.left = true;
currentY = window.pageYOffset;
})

Shortening a function to capture CTRL Keyboard events

Just in my humble opinion, the code looks to be long and repetitive, is there a way to perhaps shorten the javascript?
The use of the popular framework jQuery is also permitted:
//-------------------------------------------------------------------->>
// CTRL SHORTCUT FEATURES:
//-------------------------------------------------------------------->>
function KeyDownHandler(evnt) {
var evnt = evnt || window.event;
//CTRL-S
if (evnt.keyCode == 83 && evnt.ctrlKey) {
evnt.preventDefault ? evnt.preventDefault() : event.returnValue = false;
if (document.getElementById('save').disabled == false) {
imts_save_changes()
}
return false;
}
//CTRL-X
else if (evnt.keyCode == 88 && evnt.ctrlKey) {
evnt.preventDefault ? evnt.preventDefault() : event.returnValue = false;
if (document.getElementById('delete').disabled == false) {
imts_delete_record()
}
return false;
}
//CTRL-A
else if (evnt.keyCode == 65 && evnt.ctrlKey) {
evnt.preventDefault ? evnt.preventDefault() : event.returnValue = false;
imts_add_new()
return false;
}
}
I think somebody else can probably do a better job than me, but this is what came to my mind:
function KeyDownHandler(evnt) {
var evnt = evnt || window.event;
if (evnt.ctrlKey) {
evnt.preventDefault ? evnt.preventDefault() : event.returnValue = false;
switch(evnt.keyCode) {
case 83:
if (document.getElementById('save').disabled == false) {
imts_save_changes()
}
break;
case 88:
if (document.getElementById('delete').disabled == false) {
imts_delete_record()
}
break;
case 65:
imts_add_new()
break;
};
}
return false;
}

Clearing setTimeout issues

I'm trying to set "mouseactive" to true less than a second after a key command, but I would like to cancel that action if the key is pressed within that time period. However I can't seem to figure out how to do this. This is what I have...
$(window).keydown(function(e) {
if (e.keyCode == 40) {
e.preventDefault();
mouseactive = false;
clearTimeout(t);
var t = setTimeout("mouseActive()",800);
} else if (e.keyCode == 38) {
e.preventDefault();
mouseactive = false;
clearTimeout(t);
var t = setTimeout("mouseActive()",800);
}
});
function mouseActive() {
mouseactive = true;
}
But this doesn't work, it doesn't set mouseactive back to true... can anyone tell me what I'm doing wrong here?
Edit: Cleaned up redundant code.
More Edits: Make sure your var t is defined outside any closure including $(document).ready. See below,
var t = null;
$(document).ready(function () {
//..below code except for var t = null
});
Declare var t outside the handler.
var t = null;
$(window).keydown(function(e) {
e.preventDefault();
if (e.keyCode == 40) {
mouseactive = false;
} else if (e.keyCode == 38) {
mouseactive = false;
}
if (t != null) clearTimeout(t);
t = setTimeout(mouseActive, 800);
});
function mouseActive() {
mouseactive = true;
}
Your problem is that t is not in scope the 2nd time the function runs. You need to make t a global variable .
var t;
$(window).keydown(function(e) {
if (e.keyCode == 40) {
e.preventDefault();
mouseactive = false;
clearTimeout(t);
t = setTimeout(mouseActive,800);
} else if (e.keyCode == 38) {
e.preventDefault();
mouseactive = false;
clearTimeout(t);
t = setTimeout(mouseActive,800);
}
});
function mouseActive() {
mouseactive = true;
}
P.S. Don't pass strings to setTimeout, pass functions. It uses eval when you pass strings.
you are redeclaring "t" all the time, try this:
var t = null;
$(window).keydown(function(e) {
if (e.keyCode == 40) {
e.preventDefault();
mouseactive = false;
if(t != null)
{
clearTimeout(t);
}
t = setTimeout("mouseActive()",800);
} else if (e.keyCode == 38) {
e.preventDefault();
mouseactive = false;
if(t != null)
{
clearTimeout(t);
}
t = setTimeout("mouseActive()",800);
}
});
function mouseActive() {
mouseactive = true;
}

Categories

Resources