I'm using JavaScript and CSS to create keys that flips and play a sound. On the front side there is an image then when the key is pressed, it plays a sound and flips to revels the back which displays a different img and flips back over after the key is released. The code works for that purpose, but I want the same functionality for when someone also clicks the key. Can I have both?
function removeTransition(e) {
if (e.propertyName !== 'transform') return;
e.target.classList.remove('playing');
}
function playSound(e) {
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
if (!audio) return;
key.classList.add('playing');
audio.currentTime = 0;
audio.play();
}
const keys = Array.from(document.querySelectorAll('.key'));
keys.forEach(key => key.addEventListener('transitionend', removeTransition));
window.addEventListener('keydown', playSound);
Yes, you can have both, you can do something like this:
function removeTransition(e) {
if (e.propertyName !== 'transform') return;
e.target.classList.remove('playing');
}
function playSound(e) {
let keyCode;
if (e.type === 'click') {
keyCode = e.currentTarget.dataset.key;
} else {
keyCode = e.keyCode;
}
const audio = document.querySelector(`audio[data-key="${keyCode}"]`);
const key = document.querySelector(`div[data-key="${keyCode}"]`);
if (!audio) return;
key.classList.add('playing');
audio.currentTime = 0;
audio.play();
}
const keys = Array.from(document.querySelectorAll('.key'));
keys.forEach(key => key.addEventListener('transitionend', removeTransition));
window.addEventListener('keydown', playSound);
keys.forEach(key => key.addEventListener('click', playSound))
I've used event.type to determine if the event is a click or a keydown and used element.dataset to retrieve the appropriate key in case of click events (these events don't have the keyCode property).
Also, in case of click events, event.target is actually the clicked key, you can use that instead of looking for the key in the DOM (the querySelector(`div[data-key="${keyCode}"]`) call).
Yes, as far as I am concerned, you can have as many event listeners per object as you want. Just use the relevant event listeners and attach them to the relevant HTML objects. Remember, in Javascript you need an event listener for each event.
For example: (from https://gomakethings.com/listening-to-multiple-events-in-vanilla-js/)
This doesn't work:
document.addEventListener('click mouseover', function (event) {
// this doesn't work
}, false);
Instead, you'll have to do something like this:
var someFunction = function (event) {
// Do something...
};
// Add our event listeners
window.addEventListener('click', someFunction, false);
window.addEventListener('mouseover', someFunction, false);
Related
I need to fire an onClick only if meta(mac) / ctrl(win) is being held when it's clicked.
Here's what I tried:
const [shiftOn, setShiftOn] = useState(false)
useEffect(() => {
document.addEventListener('keydown', (e) => {
e.preventDefault()
if ((e.metaKey || e.ctrlKey) && e.code === 'KeyC') {
setShiftOn(true)
}
})
})
useEffect(() => {
document.addEventListener('keyup', (e) => {
e.preventDefault()
if ((e.metaKey || e.ctrlKey) && e.code === 'KeyC') {
setShiftOn(false)
}
})
})
const highlightCol = () => {
console.log(shiftOn) // always false
if (shiftOn) ... do something
}
const col = (
<td onClick={highlightCol} {...tdProps}>
{colName}
</td>
You're almost there -- metaKey, shiftKey, etc are what you're looking for -- but you're looking for them in the wrong place: those will simply be boolean properties on the click event itself. You do not need to check for separate keydown or keyup events on the shift or cmd keys, so can delete everything you have in a useEffect.
All you need is the single click handler:
const highlightCol = e => {
if (e.shiftKey) {
// shift key was down during the click
}
if (e.ctrlKey) {
// ctrl key was down during the click
}
}
The onClick will always be fired; just check its event's shiftKey (or metaKey or altKey or ctrlKey) properties to decide whether to do anything in it.
Different browser can have different implementations of detecting pressed keys.
I think the problem here is that you detect ctrl/meta key incorrectly.
Try to look into this service to find out what ways you have to detect particular key.
Below is what it shows for me when I press ctrl key on win.
It is better to use several methods of detecting keys to cover all browsers and platforms.
You may wrong in the way detect control key. By the way, just register event listener 1 time.
const Component =(props)=>{
const [valueFromChild, setValueFromChild] = useState('');
const [shiftOn, setShiftOn] = useState(false)
useEffect(() => {
document.addEventListener('keydown', (e) => {
console.log('dow', e);
e.preventDefault()
if (e.key === 'Control') {
setShiftOn(true)
}
})
}, [])
useEffect(() => {
document.addEventListener('keyup', (e) => {
console.log('up', e);
e.preventDefault()
if (e.key === 'Control') {
setShiftOn(false)
}
})
}, [])
const highlightCol = () => {
console.log(shiftOn) // always false
if (shiftOn) {
}
}
return <>
{shiftOn ? 'on' : 'off'}
<button onClick={highlightCol} > Click </button>
</>
}
I want to have a onkeydown event fire a function only once. for that function to fire again, the user has to release the key and press/hold again.
I know its fairly simple but I'm new at JS. Also I prefer to avoid using jQuery or other libs.
One more thing, this should work for both ie and firefox.
I'm surprised it's not mentioned, there's also event.repeat:
document.addEventListener('keydown', (e) => {
if (e.repeat) return;
console.log(e.key);
});
This will only fire once per each keypress, since event.repeat turns true after holding the key down.
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key#keyboardevent_sequence
You could set a flag:
var fired = false;
element.onkeydown = function() {
if(!fired) {
fired = true;
// do something
}
};
element.onkeyup = function() {
fired = false;
};
Or unbind and rebind the event handler (might be better):
function keyHandler() {
this.onkeydown = null;
// do something
}
element.onkeydown = keyHandler;
element.onkeyup = function() {
this.onkeydown = keyHandler;
};
More information about "traditional" event handling.
You might also want to use addEventListener and attachEvent to bind the event handlers. For more information about that, have a look at quirksmode.org - Advanced event registration models.
There's a "once" parameter you can use
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
Eg:
element.addEventListener('keydown', function(event) {
doSomething()
}, {once: true});
It'll remove it as soon as it's been called.
Alternatively you can use removeEventListener if it's a named function
Here is a method that uses addEventListener and removeEventListener
var textBox = document.getElementById("textBox");
function oneKeyDown(){
$("body").append("<h1>KeyDown<h1>"); //just to show the keypress
textBox.removeEventListener('keydown', oneKeyDown, false);
}
function bindKeyDown(){
textBox.addEventListener('keydown', oneKeyDown, false);
}
textBox.addEventListener('keyup', bindKeyDown, false)
bindKeyDown();
Code example on jsfiddle.
One note, for IE you will need to use attachEvent, detachEvent.
Here you go:
test.onkeydown = function() {
if ( this.className === 'hold' ) { return false; }
this.className = 'hold';
// call your function here
};
test.onkeyup = function() {
this.className = '';
};
Live demo: http://jsfiddle.net/simevidas/xAReL/2/
JQuery's one will help you.
What it does is, bind the eventHandler to event, and when event occurs, it runs the eventHandler and unbinds it, so that its not fired at next event.
as stated in the other answers, there is no 'onkeyfirstdown' or similar event to listen for.
the best solution is to keep track of which keys are already down in a js-object:
var keysdown = {};
element.addEventListener('keydown', function(evt) {
if(!(evt.key in keysdown)) {
keysdown[evt.key] = true;
// key first pressed
}
});
element.addEventListener('keyup', function(evt) {
delete keysdown[evt.key];
});
this way, you will not be skipping 'keyfirstpressed' events if more than one key is held down.
(many of the other solutions posted here will only fire when no other keys are down).
Here is my solution that will only run the function you pass it when a key is FIRST pressed on the target (eg window or some input field). If the user wants to trigger a key again, they'll have to release it and press it again.
Vanilla JS
const onKeyPress = (func, target = window) => {
// persistent "store" to track what keys are being pressed
let pressed = {};
// whenever a keydown event is fired ontarget element
const onKeyDown = (event) => {
// if key isn't already pressed, run func
if (!pressed[event.which])
func(event);
// add key to store
pressed = { ...pressed, [event.which]: true };
};
// whenever a keyup event is fired on the window element
const onKeyUp = (event) => {
const { [event.which]: id, ...rest } = pressed;
// remove key from store
pressed = rest;
};
// add listeners
target.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
// return a function that can be called to remove listeners
return () => {
target.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
};
};
And then to use it:
const removeListener = onKeyPress((event) => console.log(event.which + ' key pressed'))
removeListener(); // when you want to remove listeners later
React and React Hooks
import { useState } from 'react';
import { useEffect } from 'react';
import { useCallback } from 'react';
export const useKeyPress = (func, target = window) => {
// persistent "store" to track what keys are being pressed
const [pressed, setPressed] = useState({});
// whenever a keydown event is fired ontarget element
const onKeyDown = useCallback(
(event) => {
// if key isn't already pressed, run func
if (!pressed[event.which])
func(event);
// add key to store
setPressed({ ...pressed, [event.which]: true });
},
[func, pressed]
);
// whenever a keyup event is fired on the window element
const onKeyUp = useCallback((event) => {
// remove key from store
const { [event.which]: id, ...rest } = pressed;
setPressed(rest);
}, [pressed]);
useEffect(() => {
// add listeners when component mounts/changes
target.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
// cleanup/remove listeners when component unmounts/changes
return () => {
target.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
};
}, [target, onKeyDown, onKeyUp]);
};
And then to use it:
import { useKeyPress } from 'wherever';
useKeyPress((event) => console.log(event.which + ' key pressed'))
I'm trying to capture certain keydown events in my application but only if no "control" keys have been held down at the same time. I don't want to run into issues with screen reader keyboard shortcuts. Shift, Ctrl and Alt are easy to check for because they're on the javascript event, but I also need to check for Ins and Windows keys as well as any Mac control keys.
This is what I've got so far and it works as expected, but my event is still triggered when Ins or Windows is held down.
handleKeydown: function(event) {
var comboKeyPressed = event.ctrlKey || event.shiftKey || event.altKey;
if(!comboKeyPressed && event.keyCode === $.ui.keyCode.HOME) {
event.preventDefault();
this.$('>ul>li:last').attr('tabindex', -1);
this.$('>ul>li:first').attr('tabindex', 0).focus();
} else if (!comboKeyPressed && event.keyCode === $.ui.keyCode.END) {
event.preventDefault();
this.$('>ul>li:first').attr('tabindex', -1);
this.$('>ul>li:last').attr('tabindex', 0).focus();
}
}
Is there a way to check for other control keys easily or do I need to capture those events and hold onto them in some global Boolean like this.isInsertPressed?
You could do something like this:
var keysPressed = {};
var keys = { insert: 45 };
$(window).keydown(function(e) { keysPressed[e.which] = true; });
$(window).keyup(function(e) { keysPressed[e.which] = false; });
And then later:
if (keysPressed[keys.insert]) {
// insert key is currently down
}
Use event.key and modern JS!
No number codes anymore. You can check for Insert key directly.
let isInsertDown = false;
document.addEventListener("keydown", function (event) {
if (event.key === "Insert") {
isInsertDown = true;
}
});
document.addEventListener("keyup", function (event) {
if (event.key === "Insert") {
isInsertDown = false;
}
});
Mozilla Docs
Supported Browsers
Use keycode property in the Event object
if(event.keyCode === 45) // Insert Key
Is there a way for jQuery to detect that more than one key was pressed at the same time?
Is there any alternative that allows for pressing two keys at the same time to be detected?
In order to detect multiple keys being held down, use the keydown and keyup events.
var keys = {};
$(document).keydown(function (e) {
keys[e.which] = true;
});
$(document).keyup(function (e) {
delete keys[e.which];
});
I've put together a demo here: http://jsfiddle.net/gFcuU/. It's kind of fun, though I noticed my keyboard is only able to detect at most 6 keys.
It depends. For "normal" keys, that means Non- Shift, Ctrl, ALT, (CMD), the answer is no, the event handler will catch/fire in a queue, one after another.
For the modifier keys I mentioned above, there is a property on the event object.
Example:
$(document).bind('keypress', function(event) {
if( event.which === 65 && event.shiftKey ) {
alert('you pressed SHIFT+A');
}
});
Jsfiddle demo.
Other propertys are:
event.ctrlKey
event.altKey
event.metaKey
If you just want to fire a handler when several keys are pressed in series, try something like:
jQuery.multipress = function (keys, handler) {
'use strict';
if (keys.length === 0) {
return;
}
var down = {};
jQuery(document).keydown(function (event) {
down[event.keyCode] = true;
}).keyup(function (event) {
// Copy keys array, build array of pressed keys
var remaining = keys.slice(0),
pressed = Object.keys(down).map(function (num) { return parseInt(num, 10); }),
indexOfKey;
// Remove pressedKeys from remainingKeys
jQuery.each(pressed, function (i, key) {
if (down[key] === true) {
down[key] = false;
indexOfKey = remaining.indexOf(key);
if (indexOfKey > -1) {
remaining.splice(indexOfKey, 1);
}
}
});
// If we hit all the keys, fire off handler
if (remaining.length === 0) {
handler(event);
}
});
};
For instance, to fire on s-t,
jQuery.multipress([83, 84], function () { alert('You pressed s-t'); })
Nope. keypress will fire for every individual key that is pressed - except for modifier keys such as CTRL, ALT and SHIFT, you can combine them with other keys, so long as it is only one other key.
Here's a jQuery solution based on
Maciej's answer https://stackoverflow.com/a/21522329/
// the array to add pressed keys to
var keys = [];
// listen for which key is pressed
document.addEventListener('keydown', (event) => {
if ($.inArray(event.keyCode, keys) == -1) {
keys.push(event.keyCode);
}
console.log('keys array after pressed = ' + keys);
});
// listen for which key is unpressed
document.addEventListener('keyup', (event) => {
// the key to remove
var removeKey = event.keyCode;
// remove it
keys = $.grep(keys, function(value) {
return value != removeKey;
});
console.log('keys array after unpress = ' + keys);
});
// assign key number to a recognizable value name
var w = 87;
var d = 68;
var s = 83;
var a = 65;
// determine which keys are pressed
document.addEventListener('keydown', (event) => {
if ($.inArray(w, keys) != -1 && $.inArray(d, keys) != -1) { // w + d
console.log('function for w + d combo');
} else if ($.inArray(s, keys) != -1 && $.inArray(a, keys) != -1) { // s + a
console.log('function for s + a combo');
}
})
fiddle demo
https://jsfiddle.net/Hastig/us00zdo6/
If you're using esma6, you could do the following using sets.
const KEYS = new Set();
$(document).keydown(function (e) {
KEYS.add(e.which);
if(KEYS.has(12) && KEYS.has(31)){
//do something
}
});
$(document).keyup(function (e) {
KEYS.delete(e.which);
});
And if you want the user to press them together, you can do:
const KEYS = new Set(); // for other purposes
const RECENT_KEYS = new Set(); // the recently pressed keys
const KEY_TIMELAPSE = 100 // the miliseconds of difference between keys
$(document).keydown(function (e) {
KEYS.add(e.which);
RECENT_KEYS.add(e.which);
setTimeout(()=>{
RECENT_KEYS.delete(e.which);
}, KEY_TIMELAPSE);
if(RECENT_KEYS.has(37) && RECENT_KEYS.has(38)){
// Do something
}
});
$(document).keyup(function (e) {
KEYS.delete(e.which);
RECENT_KEYS.delete(e.which);
});
Here is a codepen https://codepen.io/Programador-Anonimo/pen/NoEeKM?editors=0010
As my gist expired ( no one was using it :( ) I decided to update the answer with more 2017 solution. Check below.
You can use my plugin for jquery to detect shortcuts.
It basically cache's events and get what keys are pressed at the moment. If all the keys are pressed it fires function.
https://github.com/maciekpaprocki/bindShortcut (expired!)
You have small explanation how to use it in readme file. Hope this helps. Feedback more than appreciated.
Edit 2017:
It's 2017 and we don't need jQuery plugins to solve stuff like that. In short you will need something like this:
let pressed = {};
document.addEventListener('keydown', (event) => {
pressed[event.key] = true;
});
document.addEventListener('keyup', (event) => {
delete pressed[event.key];
});
//and now write your code
document.addEventListener('keydown', (event) => {
if(pressed[firstKey]&&pressed[secondKey]){
//dosomething
}
});
Older browsers might have some quirks, however from IE9 everything should work fine except of marginal amounts of OSs that don't support right event delegation (super old ubuntu etc.). There's no way to fix that in them as that's not the browser issue.
There are some quirks in new macs connected to boolean keys like for example caps lock.
Read more: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names_and_Char_values
According to #David Tang's solution, here is a quick and dirty customization for capturing Shift+Ctrl+A combination:
var pressedKeys = {};
function checkPressedKeys() {
var shiftPressed=false, ctrlPressed=false, aPressed=false;
for (var i in pressedKeys) {
if (!pressedKeys.hasOwnProperty(i)) continue;
if(i==16){
shiftPressed=true;
}
else if(i==17){
ctrlPressed=true;
}
else if(i==65){
aPressed=true;
}
}
if(shiftPressed && ctrlPressed && aPressed){
//do whatever you want here.
}
}
$(document).ready(function(){
$(document).keydown(function (e) {
pressedKeys[e.which] = true;
checkPressedKeys();
});
$(document).keyup(function (e) {
delete pressedKeys[e.which];
});
});
Here is my code:
function pauseSound() {
var pauseSound = document.getElementById("backgroundMusic");
pauseSound.pause();
}
I would like to add a keyboard shortcut to this code, how can I do this so that the function can also be executed when a button is clicked too?
Tried to add an else if statement but it doesn't work, any ideas?
function doc_keyUp(e) {
if (e.ctrlKey && e.keyCode == 88) {
pauseSound();
}
else if (e.ctrlKey && e.keyCode == 84) {
playSound();
}
}
An event handler for the document's keyup event seems like an appropriate solution.
Note: KeyboardEvent.keyCode was deprecated in favor of KeyboardEvent.key.
// define a handler
function doc_keyUp(e) {
// this would test for whichever key is 40 (down arrow) and the ctrl key at the same time
if (e.ctrlKey && e.key === 'ArrowDown') {
// call your function to do the thing
pauseSound();
}
}
// register the handler
document.addEventListener('keyup', doc_keyUp, false);
If you want to trigger an event after pressing a key, try:
In this example press ALT+a:
document.onkeyup = function () {
var e = e || window.event; // for IE to cover IEs window event-object
if(e.altKey && e.which == 65) {
alert('Keyboard shortcut working!');
return false;
}
}
Here is a fiddle: https://jsfiddle.net/dmtf6n27/38/
Please also note there is a difference for the keycode numbers, whether you are using onkeypress or onkeyup. W3 Schools' "KeyboardEvent keyCode" Property has more information.
//For single key: Short cut key for 'Z'
document.onkeypress = function (e) {
var evt = window.event || e;
switch (evt.keyCode) {
case 90:
// Call your method Here
break;
}
}
//For combine keys like Alt+P
document.onkeyup = function (e) {
var evt = window.event || e;
if (evt.keyCode == 80 && evt.altKey) {
// Call Your method here
}
}
}
//ensure if short cut keys are case sensitive.
// If its not case sensitive then
//check with the evt.keyCode values for both upper case and lower case. ......
Here's my solution:
HTMLElement.prototype.onshortcut = function(shortcut, handler) {
var currentKeys = []
function reset() {
currentKeys = []
}
function shortcutMatches() {
currentKeys.sort()
shortcut.sort()
return (
JSON.stringify(currentKeys) ==
JSON.stringify(shortcut)
)
}
this.onkeydown = function(ev) {
currentKeys.push(ev.key)
if (shortcutMatches()) {
ev.preventDefault()
reset()
handler(this)
}
}
this.onkeyup = reset
}
document.body.onshortcut(["Control", "Shift", "P"], el => {
alert("Hello!")
})
When you call my function, it will create an array called currentKeys; these are the keys that will are being held down at that moment.
Every time a key is pressed, sensed because of onkeydown, it is added to the currentKeys array.
When the keys are released, sensed because of onkeyup, the array is reset meaning that no keys are being pressed at that moment.
Each time it will check if the shortcut matches. If it does it will call the handler.
This worked for me
document.onkeyup=function(e){
var e = e || window.event;
if(e.which == 37) {
$("#prev").click()
}else if(e.which == 39){
$("#next").click()
}
}
Catch the key code and then call your function. This example catches the ESC key and calls your function:
function getKey(key) {
if ( key == null ) {
keycode = event.keyCode;
// To Mozilla
} else {
keycode = key.keyCode;
}
// Return the key in lower case form
if (keycode ==27){
//alert(keycode);
pauseSound();
return false;
}
//return String.fromCharCode(keycode).toLowerCase();
}
$(document).ready( function (){
$(document).keydown(function (eventObj){
//alert("Keydown: The key is: "+getKey(eventObj));
getKey(eventObj);
});
});
You'll need JQUERY for this example.
These appear to all be using the deprecated keyCode and which properties. Here is a non-deprecated version using jQuery to wire up the event:
$("body").on("keyup", function (e) {
if(e.ctrlKey && e.key == 'x')
pauseSound();
else if(e.ctrlKey && e.key == 't')
playSound();
})
Note: Ctrl+t may already be assigned to opening a new browser tab.
Here's some stuff to use if you want. You can register a bunch of keys and handler with it.
Comments are in the code, but in short it sets up a listener on the document and manages a hash with the key combinations for which you want to listen.
When you register a key (combination) to listen for, you submit the keycode (preferrably as a constant taken from the exported "key" property, to which you can add more constants for yourself), a handler function and possibly an options hash where you say if the Ctrl and/or Alt key are involved in your plans for this key.
When you de-register a key (combination) you just submit the key and the optional hash for Ctrl/Alt-ness.
window.npup = (function keypressListener() {
// Object to hold keyCode/handler mappings
var mappings = {};
// Default options for additional meta keys
var defaultOptions = {ctrl:false, alt:false};
// Flag for if we're running checks or not
var active = false;
// The function that gets called on keyup.
// Tries to find a handler to execute
function driver(event) {
var keyCode = event.keyCode, ctrl = !!event.ctrlKey, alt = !!event.altKey;
var key = buildKey(keyCode, ctrl, alt);
var handler = mappings[key];
if (handler) {handler(event);}
}
// Take the three props and make a string to use as key in the hash
function buildKey(keyCode, ctrl, alt) {return (keyCode+'_'+ctrl+'_'+alt);}
function listen(keyCode, handler, options) {
// Build default options if there are none submitted
options = options || defaultOptions;
if (typeof handler!=='function') {throw new Error('Submit a handler for keyCode #'+keyCode+'(ctrl:'+!!options.ctrl+', alt:'+options.alt+')');}
// Build a key and map handler for the key combination
var key = buildKey(keyCode, !!options.ctrl, !!options.alt);
mappings[key] = handler;
}
function unListen(keyCode, options) {
// Build default options if there are none submitted
options = options || defaultOptions;
// Build a key and map handler for the key combination
var key = buildKey(keyCode, !!options.ctrl, !!options.alt);
// Delete what was found
delete mappings[key];
}
// Rudimentary attempt att cross-browser-ness
var xb = {
addEventListener: function (element, eventName, handler) {
if (element.attachEvent) {element.attachEvent('on'+eventName, handler);}
else {element.addEventListener(eventName, handler, false);}
}
, removeEventListener: function (element, eventName, handler) {
if (element.attachEvent) {element.detachEvent('on'+eventName, handler);}
else {element.removeEventListener(eventName, handler, false);}
}
};
function setActive(activate) {
activate = (typeof activate==='undefined' || !!activate); // true is default
if (activate===active) {return;} // already in the desired state, do nothing
var addOrRemove = activate ? 'addEventListener' : 'removeEventListener';
xb[addOrRemove](document, 'keyup', driver);
active = activate;
}
// Activate on load
setActive();
// export API
return {
// Add/replace handler for a keycode.
// Submit keycode, handler function and an optional hash with booleans for properties 'ctrl' and 'alt'
listen: listen
// Remove handler for a keycode
// Submit keycode and an optional hash with booleans for properties 'ctrl' and 'alt'
, unListen: unListen
// Turn on or off the whole thing.
// Submit a boolean. No arg means true
, setActive: setActive
// Keycode constants, fill in your own here
, key : {
VK_F1 : 112
, VK_F2: 113
, VK_A: 65
, VK_B: 66
, VK_C: 67
}
};
})();
// Small demo of listen and unListen
// Usage:
// listen(key, handler [,options])
// unListen(key, [,options])
npup.listen(npup.key.VK_F1, function (event) {
console.log('F1, adding listener on \'B\'');
npup.listen(npup.key.VK_B, function (event) {
console.log('B');
});
});
npup.listen(npup.key.VK_F2, function (event) {
console.log('F2, removing listener on \'B\'');
npup.unListen(npup.key.VK_B);
});
npup.listen(npup.key.VK_A, function (event) {
console.log('ctrl-A');
}, {ctrl: true});
npup.listen(npup.key.VK_A, function (event) {
console.log('ctrl-alt-A');
}, {ctrl: true, alt: true});
npup.listen(npup.key.VK_C, function (event) {
console.log('ctrl-alt-C => It all ends!');
npup.setActive(false);
}, {ctrl: true, alt: true});
It is not terribly tested, but seemed to work OK.
Look at Javascript Char Codes (Key Codes) to find a lot of keyCodes to use,
Solution:
var activeKeys = [];
//determine operating system
var os = false;
window.addEventListener('load', function() {
var userAgent = navigator.appVersion;
if (userAgent.indexOf("Win") != -1) os = "windows";
if (userAgent.indexOf("Mac") != -1) os = "osx";
if (userAgent.indexOf("X11") != -1) os = "unix";
if (userAgent.indexOf("Linux") != -1) os = "linux";
});
window.addEventListener('keydown', function(e) {
if (activeKeys.indexOf(e.which) == -1) {
activeKeys.push(e.which);
}
if (os == 'osx') {
} else {
//use indexOf function to check for keys being pressed IE
if (activeKeys.indexOf(17) != -1 && activeKeys.indexOf(86) != -1) {
console.log('you are trying to paste with control+v keys');
}
/*
the control and v keys (for paste)
if(activeKeys.indexOf(17) != -1 && activeKeys.indexOf(86) != -1){
command and v keys are being pressed
}
*/
}
});
window.addEventListener('keyup', function(e) {
var result = activeKeys.indexOf(e.which);
if (result != -1) {
activeKeys.splice(result, 1);
}
});
Explanation:
I ran into this same problem and came up with my own solution. e.metaKey didn't seem to work with the keyup event in Chrome and Safari. However, I'm not sure if it was specific to my application since I had other algorithms blocking some key events and I may have mistakenly blocked the meta key.
This algorithm monitors for keys going down and then adds them to a list of keys that are currently being pressed. When released, the key is removed from the list. Check for simultaneous keys in the list by using indexOf to find key codes in the array.
Saving with ctrl+s in React
useEffect(() => {
document.onkeydown = function (e) {
if (e.ctrlKey == true && e.key == 's') {
e.preventDefault() // to override browser's default save page feature
alert('ctrl+s is working for save!') // invoke your API to save
}
}
}, [])
Many of these answers suggest forcibly overriding document.onkeypress. This is not a good practice because it only allows for a single event handler to be assigned. If any other handlers were previously set up by another script they will be replaced by your function. If you assign another handler later, it will replace the one you assigned here.
A much better approach is to use addEventListener to attach your keyboard shortcut. This allows you to attach as many handlers as necessary and will not interfere with any external libraries that may have attached their own.
Additionally, the UIEvent.which property was never standardized and should not be used. The same goes for KeyboardEvent.keyCode. The current standards compliant property you should use to check which key was pressed is KeyboardEvent.key. Find the key you want in the full list of available values.
For best performance, return early if your desired modifier key is not pressed. As well, rather than having multiple keypress event listeners, use a single one with a swtich/case statement to react appropriately to each key that you want to handle.
Also, do not forget to cancel the default behavior of the key with Event.preventDefault if necessary. Though, there are some shortcuts that you cannot override like ctrl+w.
document.addEventListener('keypress', event => {
if (!event.ctrlKey) { return; }
event.preventDefault();
switch (event.key) {
case 'x' : doSomething(); break
case 'z' : doSomethingElse(); break;
default : console.log('unhandled key was pressed');
}
});