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...
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
$().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
Related
So I'm trying something out, if you have two functions you want to call after the same key press like so:
var plus = function () {
document.addEventListener("keyup", function(e) {
if (/[+]/g.test(e.key)) {
console.log("plus");
}
})
}
plus();
var minus = function() {
document.addEventListener("keyup", function(e) {
if (/[-]/g.test(e.key)) {
console.log("minus");
}
});
}
minus();
function check() {
document.addEventListener("keyup", function(e) {
if(plus) {
if (e.keyCode == 13) {
console.log("enter pressed after plus");
plus = false;
minus = function() {
document.addEventListener("keyup", function(e) {
if (/[-]/g.test(e.key)) {
console.log("minus");
}
});
}
}
} else if(minus) {
if (e.keyCode == 13) {
console.log("enter pressed after minus");
minus = false;
plus = function () {
document.addEventListener("keyup", function(e) {
if (/[+]/g.test(e.key)) {
console.log("plus");
}
})
}
}
}
});
}
check();
If you press minus first then enter console.log("enter pressed after plus") always gets called first because of the code's order, even though what I want to do is that I want the enter to correspond to the key I'm pressing first, if I press plus first then I want console.log("enter pressed after plus") to get called, and if I press minus first then I want console.log("enter pressed after minus") to get called.
Any help would be appreciated and thanks in advance.
Oh also sorry about the stupid title, couldn't think of a better one.
To clean this up a bit (keep it DRY) you can move all the event handler logic into a single function and use a single listener.
To keep track of the last pressed key we can use a variable defined in the function's outer scope. And, update it after each event. Then, when "Enter" is pressed we can check what the last key was and log accordingly.
Also, the KeyboardEvent.keyCode property is depreciated. You should use KeyboardEvent.code property instead.
Example
const input = document.querySelector('input')
const log = document.getElementById('log')
function check() {
let lastKey = null
input.addEventListener('keyup', ({ key }) => {
if (['+', '-', 'Enter'].includes(key)) { // we only care about these keys
if (key === '+') log.textContent = 'plus'
if (key === '-') log.textContent = 'minus'
if (key === 'Enter') { // `Enter` was keyed, what was keyed last?
if (lastKey === '+') log.textContent = 'enter pressed after plus'
if (lastKey === '-') log.textContent = 'enter pressed after minus'
}
lastKey = key // current key becomes last key
}
})
}
check()
<input placeholder="Click here, then press and release a key." size="40">
<p id="log"></p>
I am building a web app where I detect the headphones button event. I succeeded in capturing headphones button event when they are plugged in. Now I am trying to capture Bluetooth headphones next button event. Any help on this please?
Code for headphone button detection.
document.addEventListener('volumeupbutton', () => {
//Do something here
}, false);
I need something similar to this.
You can use keydown and keyup events for implementing the long press functionality.
// Imprementation of Long Press
const longPressTime = 1500;
let keyDownTimeout;
document.addEventListener('keydown', e => {
if (keyDownTimeout) {
return;
}
keyDownTimeout = setTimeout(() => {
// button was held for 1500ms, consider it a long-press
if (e.code === 'ArrowUp') {
console.log("Action Performed");
// do long-press action
} else {
console.log("Other action performed");
}
}, longPressTime);
});
document.addEventListener('keyup', e => {
clearTimeout(keyDownTimeout);
keyDownTimeout = 0;
});
Press any key
The above methods work for single key long press. Refer to KeyCode for key code.
Demo of above
I don't believe using the built-in volumeupbutton event will allow you to detect how long the click was, to determine if it should be treated as volume-up or skip-track. Instead you should be able to use the keyup/keydown events, combined with the keyCode property to determine if it is the volume button, like this:
const longPressTime = 1500;
let volumeUpButtonTimeout;
const volumeButtonKeyCode = 0; // you'll need to determine the key code
// cross platform way to get the key code
const getKeyCode = e => {
if (e.key !== undefined) {
return e.key;
} else if (e.keyIdentifier !== undefined) {
return e.keyIdentifier;
} else if (e.keyCode !== undefined) {
return e.keyCode;
}
}
document.addEventListener('keydown', e => {
if (getKeyCode(e) == volumeButtonKeyCode) {
volumeUpButtonTimeout = setTimeout(() => {
// button was held for 1500ms, consider it a long-press
// do long-press action
}, longPressTime)
}
});
document.addEventListener('keyup', e => {
if (getKeyCode(e) == volumeButtonKeyCode) {
clearTimeout(volumeUpButtonTimeout);
}
});
You could use this code to determine what keyCode corresponds to the volume up button:
document.addEventListener('keyup', e => {
console.log(e.keyCode);
});
I'm trying to implement a search as you type feature on my site. I'm working on the front-end side right now and using mockjax to pull in fake data.
My problem: When the drop down menu pops up you have the option to go over your choices (which highlight in yellow). I realized today though that if your using the arrow keys to scroll through your choices and move your mouse over the menu then it will cause two options to be highlighted! I don't want to confuse my users so I only want it to highlight one at a time. If they are using their keyboard and hover over with the mouse than the keyboard selection would jump to where the mouse is.
(In case I'm not being clear and you need an example, go to amazon and use their search with your arrow keys and then hover the mouse over an option, it changes. I want it like that!)
Most of the html, css and mockjax can't be included in this fiddle so it looks funky- but just case someone needs to see my code.
http://jsfiddle.net/2JGHu/
(function (Backbone, _, context) {
"use strict";
var SuggestiveSearch = Backbone.View.extend({
tracking: function (action, label) {
_gaq.push(['_trackEvent', 'SearchAsYouType2.0', action, label]);
},
fetchTemplate: function (name) {
var callback = _.bind(function (template) {
this.template = tmpl(template);
}, this);
$.get("/js/templates/" + name + ".html", callback);
},
close: function () {
this.$suggestionList.addClass("hide");
this.tracking('Close', 'Clicked-Off');
// Reset our list selection index
this.model.set("currentIndex", null);
},
open: function () {
this.$suggestionList.removeClass("hide");
this.tracking('Open', 'Clicked-On');
},
preventCloseHandler: function (e) {
e.stopPropagation();
},
directionSelectionHandler: function (keyCode) {
var currentIndex = this.model.get("currentIndex"),
incr = keyCode === 40 ? 1 : -1,
newIndex = currentIndex + incr,
choicesLen = this.$choices.length - 1,
isOutOfRange = newIndex > choicesLen || newIndex < 0;
// If index is out of range set it either to the first or last choice
if (isOutOfRange) {
newIndex = newIndex < 0 ? choicesLen : 0;
}
// Remove previous selected
// class on li's
this.$choices
.removeClass("is-selected");
this.$choices
.eq(newIndex)
.addClass("is-selected");
// Store our index
this.model.set("currentIndex", newIndex);
},
enterHandler: function (e) {
var currentIndex = this.model.get("currentIndex");
if (currentIndex !== 0) {
this.tracking('Enter', 'Selected-Choice');
window.location = this.$choices.eq(currentIndex).find("a").attr('href');
}
},
keyDownHandler: function (e) {
var keyCode = e.which,
isArrowKeys = keyCode === 40 || keyCode === 38;
if (!isArrowKeys) {
return;
}
e.preventDefault();
},
keyUpHandler: function (e) {
var $input = $(e.currentTarget),
query = $input.val(),
keyCode = e.which;
switch (keyCode) {
case 40:
case 38:
this.directionSelectionHandler(keyCode);
this.tracking('Keyboard navigate', 'Selected-Choice');
e.preventDefault();
break;
case 13:
this.enterHandler(e);
break;
default:
this.model.set("query", query);
}
},
choiceClickHandler: function (e) {
this.tracking('Click', 'Selected-Choice');
e.stopPropagation();
},
render: function () {
this.$suggestionList
.html(this.template(_.pick(this.model.attributes, "ProductSuggestions", "FilterSuggestions")));
// Store our list of choices but also add our already cached input to that collection
this.$choices = this.$suggestionList.find(".autocomplete__choice", this.$el).add(this.$input);
this.open();
},
events: {
"keyup input": "keyUpHandler",
"keydown input": "keyDownHandler",
"click .autocomplete__choice": "choiceClickHandler",
"click": "preventCloseHandler"
},
bindings: function () {
this.listenTo(this.model, "sync", this.render);
$(document).on('click', _.bind(this.close, this));
},
initialize: function () {
this.fetchTemplate("suggestions");
this.$suggestionList = this.$el.find(".autocomplete");
this.$input = this.$el.find("input");
this.bindings();
}
});
context.Views = context.Views || {};
context.Views.SuggestiveSearch = SuggestiveSearch;
}(Backbone, _, =|| {}));
Let me know if I need to include anymore information. Thank you in advance!
Since your JSFiddle doesn't produce the behavior, it's not easy for me to write code that solves your problem, but I can give you advice that might help you do it yourself.
The way I recommend solving this issue is by removing the .hover highlighting in your CSS and implementing a function that adds the class is-selected to an object when it is being hovered over and removing the class from all other elements. That way it will be compatible with your current directionSelectionHandler:
It looks like key-press can only be executed on a focus element? I don't fully buy into that, there has to be a way to execute a key-press event similar to a click event?
I have a view that works with one item at a time. I have a mouseenter - mouseleave function that adds a class to the item the mouse is over. When the item receives that class I want to be able to use a key-press event to run a function on that item.
Obviously this is a slight obstacle but Id like to find out what I need to do. Below is an example view.
var PlayerView = Backbone.View.extend({
tagName: 'div',
events: {
'click .points, .assists, span.rebounds, span.steals':'addStat',
'mouseenter': 'enter',
'mouseleave': 'leave',
'keypress': 'keyAction'
},
enter: function() {
this.$el.addClass('hover');
},
leave: function() {
this.$el.removeClass('hover');
},
keyAction: function(e) {
var code = e.keyCode || e.which;
if(code == 65) {
alert('add assist')
}
}
});
So there isn't much logic here, but I am thinking I would write something like this
keyAction: function(e) {
var code = e.keyCode || e.which;
if(code == 65) {
var addAssist = parseInt(this.model.get('assists')) + 1;
this.model.save({assists: addAssist});
}
}
Basically If I could figure out how to fire that keyAction method I should be good to go. So what are some caveats I am missing in executing some code like this? I am sure there are a few.
I do understand some of what is wrong with this code, it has no way of knowing when we run keypress in that view, I would have to add a conditional or something to find the active class, so when I execute the keypress it knows what model I am talking about, very vague description here but I get there is something wrong I am just not sure how to do this?
My solution
initialize: function() {
this.listenTo(this.model, "change", this.render);
_.bindAll(this, 'on_keypress');
$(document).bind('keydown', this.on_keypress);
},
enter: function(e) {
this.$el.addClass('hover');
},
leave: function(e) {
this.$el.removeClass('hover');
},
on_keypress: function(e) {
// A for assist
if(e.keyCode == 65) {
if(this.$el.hasClass('hover')) {
var addThis = parseInt(this.model.get('assists')) + 1;
this.model.save({assists: addThis});
}
}
// R for rebound
if(e.keyCode == 82) {
if(this.$el.hasClass('hover')) {
var addThis = parseInt(this.model.get('rebounds')) + 1;
this.model.save({rebounds: addThis});
}
}
// S for steal
if(e.keyCode == 83) {
if(this.$el.hasClass('hover')) {
var addThis = parseInt(this.model.get('steals')) + 1;
this.model.save({steals: addThis});
}
}
// 1 for one point
if(e.keyCode == 49) {
if(this.$el.hasClass('hover')) {
var addMake = parseInt(this.model.get('made_one')) + 1;
this.model.save({made_one: addMake});
var addOne = parseInt(this.model.get('points')) + 1;
this.model.save({points: addOne});
}
}
// 2 for two points
if(e.keyCode == 50) {
if(this.$el.hasClass('hover')) {
var addMake = parseInt(this.model.get('made_two')) + 1;
this.model.save({made_two: addMake});
var addTwo = parseInt(this.model.get('points')) + 2;
this.model.save({points: addTwo});
}
}
// 2 for two points
if(e.keyCode == 51) {
if(this.$el.hasClass('hover')) {
var addMake = parseInt(this.model.get('made_three')) + 1;
this.model.save({made_three: addMake});
var addThree = parseInt(this.model.get('points')) + 3;
this.model.save({points: addThree});
}
}
}
This is cool for my app because when the user hovers over the item the user can hit a key to add data, instead of clicking.
So you are only going to be able to listen to the keypress in whichever element that you have the listener set on (or its children). And the keypress event is only going to fire if the element is focused. So I think the best solution for you would be to set focus on the element you are hovering over, then you can listen for the keypress, or better yet, listen to keydown because it behaves in a more standard way cross browser.
Here is a working JSFiddle demonstrating this technique: http://jsfiddle.net/DfjF2/2/
Only certain form elements accept focus. You can add contenteditable or tabindex attributes to the element, and that should allow pretty much any element to receive focus, but then the keydown event won't actually get fired! This is a browser specific issue. In my experience, a <span> will cause keydown and keyup events to be fired in every browser I have tested (Chrome, Firefox, IE, Safari, Android browser, Silk). So in the jsfiddle I added a span inside the target element, put focus on that, and added the keydown event listener to it.
So if you added an empty <span> into your view, your code could look something like this:
var PlayerView = Backbone.View.extend({
tagName: 'div',
events: {
'click .points, .assists, span.rebounds, span.steals':'addStat',
'mouseenter': 'enter',
'mouseleave': 'leave',
'keydown': 'keyAction'
},
enter: function() {
this.$el.addClass('hover');
var span = this.$el.find('span');
span.attr('tabindex', '1').attr('contenteditable', 'true');
span.focus();
},
leave: function() {
this.$el.removeClass('hover');
var span = this.$el.find('span');
span.removeAttr('contenteditable').removeAttr('tabindex');
span.blur();
},
keyAction: function(e) {
var code = e.keyCode || e.which;
if(code == 65) {
alert('add assist')
}
}
});
I'd like to trigger an event once after key down and a different event only after the down arrow key has been released, like so:
$('body').keydown(function (e)
{
if(e.keyCode==40)
{
//do something
}
$('body').keyup(function (d)
{
if(d.keyCode==40)
{
//do something else
}
}
}
This code only functions partially. The keydown is triggered continuously as the down arrow key is held.
I have a setInterval whose refresh rate I'm altering when I hold the arrow key. Unforunately setTimeOut isn't an option in this situation.
So my code looks something like this:
clearInterval(interval);
refresh = 100;
interval();
$('body').keydown(function (e) {
if(e.keyCode==40) {
//do something
}
return false;
})
.keyup(function(e) {
if(e.keyCode==40) {
//do something else
}
return false;
});
$('body').on('keyup', function (e) {
if(e.keyCode==40) {
//do something
}
// after first keyup set to handle next keydown only once:
$(this).one('keydown', function(e) {
if(e.keyCode==40) {
//do something else
}
});
});
If you need exactly trigger the event and not handle as it's in your example, then you need to use $.trigger() method.
If you want to do some action only once while the key remains pressed, simply keep track of that:
var arrowKeyDown = false;
$('body').keydown(function(e) {
if (e.which == 40 && !arrowKeyDown) {
arrowKeyDown = true;
// ...
}
});
$('body').keyup(function(e) {
if (e.which == 40) {
arrowKeyDown = false;
}
});
Demo: http://jsfiddle.net/utfwQ/
$('body').keydown(function (e)
{
console.log('down');
}).keyup(function(e){console.log('up')});
If you really need to remove the keyup listener when you're done,
http://jsfiddle.net/CgmCT/
document.body.addEventListener('keydown', function (e) {
if(e.keyCode === 40){
console.log('key 40 down');
// key down code
document.body.addEventListener('keyup', function listener(d){
if(d.keyCode === 40){
document.body.removeEventListener('keyup', listener, true);
console.log('key 40 up');
// key up code
}
}, true);
}
}, true);