keydown + keyup events for specific keys
Asked Answered
E

2

13

I'm trying to make the background color change when certain keys are held down. For example, when the 'r' key is being held down, the background should be red. When the 'r' key is not being pressed anymore, the background should default to white.

$(document).ready(function () {
    $('body').keydown(function(e){
        if(e.keyCode == 114){
            $(this).css({'background':'red'});  
        }
        if(e.keyCode == 121){
            $(this).css({'background':'yellow'});
        }
    });
    $('body').keypress(function(e){
        if(e.keyCode == 114){
            $(this).css({'background':'red'});  
        }
        if(e.keyCode == 121){
            $(this).css({'background':'yellow'});
        }
    });
    $('body').keyup(function(e){
        if(e.keyCode == 114){
            $(this).css({'background':'white'});
        }
        if(e.keyCode == 121){
            $(this).css({'background':'white'});
        }
    });

});

The problem I'm having is that keyup is not working specifically for each individual key.

    $('body').keyup(function(e){
        $(this).css({'background':'white'});
    });

I know if I remove the if conditionals from keyup altogether then it will behave how I said I wanted it to — but I want to be able to do different things later on using keyup with specific keys. For example, when just the 'b' key is released, maybe it will say something on the screen like "You just released the b key!" How can I keep track of keydown and keyup events for specific keys and make different things happen for each? I know this isn't very organized either (I'm pretty new to this stuff) so if there's a completely different and better way of doing this...

Errick answered 2/5, 2013 at 19:7 Comment(1)
have you tried using e.which instead of e.keyCode? https://mcmap.net/q/22253/-jquery-event-keypress-which-key-was-pressedElga
H
20

Handle Keyboard in JavaScript

1. List of Action functions

Create an Object literal list with your desired functions. Say you have a character you want to move, here are some example Actions:

const Action = {
  powerOn()  { console.log("Accelerating..."); },
  powerOff() { console.log("Decelerating..."); },
  brakeOn()  { console.log("Break activated"); },
  brakeOff() { console.log("Break released");  },
  exit()     { console.log("Nice drive!");     },
  // clutch, colors, lights, fire... Add more, go wild!
};

PS: In a real-case scenario every single function would contain the actual logic to handle the character, being it a one-time "move-by-N-px", or act as a proxy to populate a queue which is than consumed by a frame-rate engine like Window.requestAnimationFrame. You can also create functions to change colors, etc. You got the general idea.

2. Associate Keys to Actions by Event.type

Associate KeyboardEvent.key to the desired Action for a desired Event.type (←must be lowercase):

const keyAction = {
  w:      { keydown: Action.powerOn,  keyup: Action.powerOff },
  s:      { keydown: Action.brakeOn,  keyup: Action.brakeOff },
  Escape: { keydown: Action.exit }
};

Notice that the key-names "w" "s" "Escape" are represented as the returned value of the preferred KeyboardEvent.key, instead of the numeric KeyboardEvent.keyCode. We're humans, not robots.

3. KeyboardEvent handler

Finally, let's listen to the "keyup" "keydown" Events and trigger a callback function keyHandler, that will eventually trigger our specific Action function, say i.e: keyAction["w"]["keydown"]() which is actually our spaceship's powerOn Action function!

const keyHandler = (ev) => {
  if (ev.repeat) return; // Key-held, prevent repeated Actions (Does not work in IE11-)
  if (!(ev.key in keyAction) || !(ev.type in keyAction[ev.key])) return; // No such Action
  keyAction[ev.key][ev.type]();  // Trigger an Action
};

['keydown', 'keyup'].forEach((evType) => {
    document.body.addEventListener(evType, keyHandler);
});

Result:

const Action = {
  powerOn()  { console.log("Accelerating..."); },
  powerOff() { console.log("Decelerating..."); },
  brakeOn()  { console.log("Break activated"); },
  brakeOff() { console.log("Break released");  },
  exit()     { console.log("Nice drive!");     },
};

const keyAction = {
  w: { keydown: Action.powerOn,  keyup: Action.powerOff },
  s: { keydown: Action.brakeOn,  keyup: Action.brakeOff },
  Escape: { keydown: Action.exit }
};

const keyHandler = (ev) => {
  if (ev.repeat) return;                             
  if (!(ev.key in keyAction) || !(ev.type in keyAction[ev.key])) return;
  keyAction[ev.key][ev.type]();
};

['keydown', 'keyup'].forEach((evType) => {
  document.body.addEventListener(evType, keyHandler);
});
Click here to focus this window.<br>
Then, use [<kbd>W</kbd>], [<kbd>S</kbd>] or [<kbd>Esc</kbd>] keys on your keyboard.

Example for your specific request:

const changeBG = (color) => document.body.style.background = color;

const Action = {
  red()    { changeBG("#f00"); },
  yellow() { changeBG("yellow"); },
  orange() { changeBG("orange"); },
  reset()  { changeBG(""); },
};

const keyAction = {
  r: { keydown: Action.red,    keyup: Action.reset },
  y: { keydown: Action.yellow, keyup: Action.reset },
  o: { keydown: Action.orange }, // No keyup for this one :)
};

const keyHandler = (ev) => {
  if (ev.repeat) return;  
  if (!(ev.key in keyAction) || !(ev.type in keyAction[ev.key])) return;
  keyAction[ev.key][ev.type]();
};

['keydown', 'keyup'].forEach((evType) => {
  document.body.addEventListener(evType, keyHandler);
});
body { transition: background: 0.3s; }
Click here to focus this window. <br>Keys:<br>
[<kbd>Y</kbd>] for Yellow<br>
[<kbd>R</kbd>] for Red<br>
[<kbd>O</kbd>] to permanently set to Orange
Hospitaler answered 2/5, 2013 at 19:15 Comment(11)
let's say I wanted to reveal a hidden object when the b key is released and make another object move when a different key is released - how would that work with this? I want to be able to do different things for the keyup and keydown of individual keys, not just change colors.Errick
thanks! I guess what I really want is the ability to use different functions within the keydown and keyups for specific keysErrick
I am leaving for work in a few minutes — I will definitely check back here when I get home and look at your newer example thanks againErrick
This is excellent! What would I do if I wanted to add a css class [.addClass()] to an element on keydown and remove that class [.removeClass()] or add another class on keyup? For example, if I pressed the 'z' key (90), it would be something like: 90 : ['#div', 'addClass', "?", ['addedclass?','removedclass?'] ] ???Errick
@luke so basically you need to set our var method to be ["addClass"] on keydown and ["removeClass"] on keyup. Hope it makes sense.Hospitaler
This makes sense for dealing with the one class being added on keydown and removed on keyup — what would I do if I not only wanted to remove the added class on keyup but also add another class or a few more classes on keyup? Or multiple classes added on keydown or even remove a class on keydown in some cases? Some kind of a template for handling a lot of these would help a lot. I really appreciate your help with this by the way.Errick
@luke may I know for what is all about? what you want to accomplish exactly ?Hospitaler
Well I tried to make a simple example just for this question to demonstrate what I'm trying to do with jquery and find a solution — but what I'm really trying to do is make a simple platform game that uses jquery to work. I plan on making it work by using a bunch of css classes, which contain various animations and transitions, and adding and removing them from elements using jquery.Errick
For example, when the forward key is pressed, a 'walking' class would be added to the character and the 'idle' class would be removed. The background would also get a 'moving' class to start scrolling in the right direction. I could go on and on with all of the different classes I'd make for the character and environment and how I would fake collision detectionErrick
Awesome I'm curious as to how you would handle this now that you know what I'm trying to accomplish. My plan for the addition and removal of classes was mostly for handling all of the different sprite animations that the character has (walking, running, jumping, crouching, smashing, dying, etc) as well as the various moving things like enemies and objects — I think rather than making the actual character move left to right, the stage itself would actually be moving behind the character, with the character just slightly nudging on screen in the direction that they're 'moving'.Errick
'collision detection' would basically be something like this jsfiddle.net/98sAG — it would constantly be looking for elements that overlap and call various functions. I figured I'd make sections all across the stage which would effect the character depending on the contents in that area — if the character enters a section where there's a platform above that can be jumped on, setinterval would of caught that the character entered that section and added a 'platform jump' class which starts off as a regular jump but lands higher to make the character appear standing on the platformErrick
S
-1


$().ready(function() {
  $('body').on("keyup keydown", function() {
    if(e.keyCode == 114 || e.keyCode = 121) {
      $(this).toggleClass("key" + e.keyCode)
    }
  })
})


Now just match the css rules with your css classes

Solemnize answered 2/5, 2013 at 19:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.