Related
MDN states that KeyboardEvent.which is deprecated. How can I substitute it for a non-deprecated version?
For example, I have the following:
window.onkeydown = (event) => { console.log(event.which); }
I thought event.key.charCodeAt() could substitute event.which, but this won't work for keys such as ALT, CTRL or ENTER, and it only works if event.key.length === 1:
window.onkeydown = (event) => { console.log(event.key.charCodeAt()); }
To recap, event.which != event.code and event.which != event.key, therefore I am unable to simply use event.key.
Is there a substitute for event.which which detects combination keypresses including ALT, CTRL or ENTER?
TL;DR: These are the rules you should follow:
When getting text input from the user, use the keypress event along with e.key
For shortcuts and other combinations, the built-in way is to use keydown/keyup and check the various modifier keys. If you need to detect chords, you may need to build a state machine.
Background
Keyboard input is split into two phases - keydown/keyup pairs, which track physical keys being pressed, and composed characters that combines multiple sequences of keys to compute a character.
Getting "text"
If you want to know what the operating system thinks the composed sequence is, you should use KeyboardEvent.key
Sample code:
document.getElementById('a').addEventListener('keypress', e => console.log(e.key));
<input id="a" type="text" placeholder="type here">
The reason you want to do this most of the time is because many languages compose characters with several keypresses. The easiest for US-101 keyboards to understand is pressing the shift key + a is A, compared to just pressing a. For languages like Russian, with the altgr dead key, this becomes especially important.
The point I am trying to make is that doing all of this work yourself - detecting key sequences and determining the correct text output is a hard problem™. It is the job of the operating system for a reason.
Now, for older browsers, you may not want to use e.key for lack of older support. Then you can fall back to things like which, or other non-standard approaches.
At some point in the future, keypress may be removed by browsers. The beforeinput event is supposed to be the replacement. However, that event
is only supported in chrome, so I'm omitting in here for brevity's sake.
Getting keystrokes
Now then, suppose you are not tracking text, but rather key sequences. This is for things like games, or listening to ctrl-c etc. In this case, the correct thing to do is to listen to keydown/keyup events. For modifier keys, you can simply listen to the ctrlKey, shiftKey, and metaKey properties of the event. See below:
document.getElementById('a').addEventListener('keydown', (e) => {
const states = {
alt: e.altKey,
ctrl: e.ctrlKey,
meta: e.metaKey,
shift: e.shiftKey,
};
const key = e.key;
const code = e.code;
console.log(`keydown key: ${key}, code: ${code}`, states);
});
<input id="a" type="text" placeholder="press ctrl">
As an example, when pressing shift-o on my keyboard, I get the following:
keydown key: Shift, code: ShiftLeft {
"alt": false,
"ctrl": false,
"meta": false,
"shift": true
}
keydown key: O, code: KeyS {
"alt": false,
"ctrl": false,
"meta": false,
"shift": true
}
Hopefully the states part is pretty self-evident. They say whether that modifier key was pressed while the other key is down.
The difference between key and code has to do with keyboard layouts. I am using the software dvorak layout. Thus when I type the s key, the scan code that goes to the operating system says s, but then the OS converts that to o because it's dvorak. Code in this case always refers to the scan code (physical key being pressed), while the key corresponds to the operating system's best-effort to figure out what the "text" will be. This isn't always possible, especially with other languages. Again, this is why using the key for the keypress is the right way to go about it.
3rd party libraries
If this doesn't sound particularly easy, that's because it's not. The last time I was looking at this, I came across the mousetrap library, although I'm not sure I would recommend it, given some of the issues I found. It does, however, show an example of building a state machine to track key chords.
Addendum
This is also why you need to track keydown/keyup if you want to eat keystrokes. Since there is no "text" for ctrl+c, you won't get a proper keypress, and thus the browser will natively handle it. If you want to run your own behavior, you need to e.preventDefault() on the keydown itself. (Some of the followup events like copy can also be cancelled, but that's not universally true)
If you also just need to track keys inserted after-the-fact into an input field (or contenteditable div), see the input event.
History:
Updated 8/2019 to change keypress->beforeinput
As the other answers pointed out, event.which has one main problem: it does not return the same number for different browsers or computers (maybe this is why it is deprecated). Therefore, there is no perfect substitute for it, since it will output different numbers for different users.
So the main problem in trying to create a substitute for it (let's name it: function whichSubstitute(event)) is that the Meta and Shift keys, for example, don't have a unique number that whichSubstitute should get when one of them is pressed, it varies according to OS.
With that in mind, there are two approaches for getting the unicode code point for the user's input.
Getting the unicode value for the character that the user inputted (e.g., ü, which would be 'ü'.codePointAt(0)).
Getting a numeric value for the character that corresponds to the physical key pressed in the keyboard, which might be different from what was inputted to the text field. As AnilRedShift mentioned, the keyboard layout might change the "natural" output from that key in the keyboard, in such a way that the key s might output o. In this case, we'd get 's'.codePointAt(0), instead of getting the value for 'o' (that is, what was actually outputted), like we would get using the first approach. More on this from MDN:
For example, the code returned is "KeyQ" is for the "q" key on a QWERTY layout keyboard, but the same code value also represents the "'" key on Dvorak keyboards and the "a" key on AZERTY keyboards. That makes it impossible to use the value of code to determine name of the key is to users if they're not using an anticipated keyboard layout.
In short: approach number 1 gets the unicode code point for ü, whereas approach number 2 gets the code points for SHIFT, 6 and U (since SHIFT+6+U == ü).
In this answer, we'll use String.prototype.codePointAt() instead of String.prototype.charCodeAt(). The differences are well explained here. The reason is that we can get the whole unicode number with .codePointAt(0), whereas the .charCodeAt(0) would lack .codePointAt(1) to complete the UTF-16 encoded code point.
For approach number 1, we can use the following code:
function whichSubstitute(event) {
const theKey = event.key;
if (theKey.length === 1) {
return theKey.codePointAt(0);
}
switch (theKey) {
case "Backspace":
return 8;
case "Tab":
return 9;
case "Enter":
return 13;
case "Alt":
return 18;
case "Escape":
return 27;
case "Delete":
return 127;
case "Dead": //As of july 2018, Firefox has no support for "Dead" keys https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
{}
break;
case "Unidentified":
alert("handle the 'Unidentified' if you want to!");
}
/*** there are many other possible key values https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
but, AFAIK, there are no unicode code points for them, such as:
switch (theKey) {
case "AltGraph":
case "CapsLock":
case "Control":
case "Fn":
case "FnLock":
...
event.which may output some number for them, but it is not consistent across
browsers/machines and they may overlap with other code points. For example:
case "ArrowUp":
return 38; //this overlaps with "&"
case "ArrowLeft":
return 37; //this overlaps with "%"
case "ArrowDown":
return 40; //this overlaps with "("
case "ArrowRight":
return 39; //this overlaps with "'"
***/
return 0;
}
//test
document.onkeydown = (event) => {
console.log('whichSubstitute: ' + whichSubstitute(event) + '; event.which: ' + event.which);
//note that whichSubstitute gets the ASCII number of 'a', while event.which only gets the ASCII number of 'A' (upper case, always)
}
This, of course, does not solve the problem of getting only one unique consistent number for a pressed key when there is no unicode code point for it (as in the case of Meta). Such keys need to be handled by the programmer according to her/his needs.
For approach number 2, we can use the following code:
function whichSubstitute(event) {
const theChar = event.code;
if (theChar.startsWith('Key')) {
return theChar.codePointAt(3);
}
if (theChar.startsWith('Digit')) {
return theChar.codePointAt(5);
}
switch (theChar) {
case "Backspace":
return 8;
case "Tab":
return 9;
case "Enter":
return 13;
case "Alt":
return 18;
case "Escape":
return 27;
case "Delete":
return 127;
case "Minus":
return 45;
case "Plus":
return 43;
case "Equal":
return 61;
case "Delete":
return 127;
case "BracketRight":
return 93;
case "BracketLeft":
return 91;
case "Backslash":
return 92;
case "Slash":
return 47;
case "Semicolon":
return 59;
case "Colon":
return 58;
case "Comma":
return 44;
case "Period":
return 46;
case "Space":
return 32;
case "Quote":
return 34;
case "Backquote":
return 39;
//there are also "Numpad....." variants
case "Unidentified":
alert("handle the 'Unidentified' if you want to!");
}
/*** there are many other possible character values https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code
but, AFAIK, there are no unicode code points for them, such as:
switch (theKey) {
case "AltLeft":
case "CapsLock":
case "ControlRight":
case "Fn":
case "NumpadDecimal":
...
event.which may output some number for them, but it is not consistent across
browsers/machines and they may overlap with other code points.
***/
return 0;
}
//test
document.onkeydown = (event) => {
console.log('whichSubstitute: ' + whichSubstitute(event) + '; event.which: ' + event.which);
}
This second approach might be useless, since the same physical key might output different unicode characters according to different keyboard layouts. The users might have no idea of which key they should press.
Related: https://www.w3.org/TR/uievents/#keys
From the specification:
which of type unsigned long, readonly
which holds a system- and implementation-dependent numerical code signifying the unmodified identifier associated with the key pressed. In most cases, the value is identical to keyCode
keyCode of type unsigned long, readonly
keyCode holds a system- and implementation-dependent numerical code signifying the unmodified identifier associated with the key pressed. Unlike the KeyboardEvent.key attribute, the set of possible values are not normatively defined in this specification. Typically, these value of the keyCode should represent the decimal codepoint in ASCII [RFC20][US-ASCII] or Windows 1252 [WIN1252], but may be drawn from a different appropriate character set. Implementations that are unable to identify a key use the key value '0'.
See Legacy key models for more details on how to determine the values for keyCode.
(I have omitted the links)
So it is quite easy to create a version that is compatible with the specification. The easiest version just returns 0 for each key.
A slightly more involved version takes event.key and grabs it's ASCII number. You can do the same for the control keys (altKey, ctrlKey, shiftKey).
In short, as it stands the behavior of which is different between systems and browsers. Using the non-deprecated event information you can create a more robust version that removes these differences and will be more future proof.
You can check the behavior of your version with the implementation of which in major browsers. If you are not using any edge cases your version will be both simple and compatible.
I have a textarea. After writing some misspelled text and using rightclick -> correction the word gets replaced with a correctly spelled word.
Now, my problem here is that i need to exectue some javascript code when the correction gets done.
How can i catch the firefox spellcheck correction event?
If there is a only a solution using a firefox Add-On i would be happy too to know that one.
Mozilla fires oninput in this case, didn't test in others, but should work everywhere.
Interestingly enough, FF seems to fire two input events when using spelling correction: it deletes the word first, and then inserts the new one:
> value=[holy coww]
(right click and choose "cow")
> value=[holy ]
> value=[holy cow]
http://jsfiddle.net/7ssYq/
I was originally going to suggest the oninput event, like thg435's answer, but I thought I'd fish for more details in the comments first. If you don't need to differentiate between spell checker corrections and other types of input (keyboard, paste, drag and drop, etc), then oninput would do the job just fine.
If you do want to differentiate between those types of input, then I'm afraid there's no event that fires specifically for spell checker corrections. However, there are events for most other types of input, so you could at least narrow down the likelihood of your input event being a correction if you check for other types of event first. Consider the following:
(function () {
var el = document.getElementById("MyInput"),
ignore = false;
el.oninput = function (e) {
// ignore the events that we don't need to capture
if (ignore) {
ignore = false;
return true;
}
// Your code here
}
// IIRC, you need the following line for the `ondrop` event to fire
el.ondragover = function () { return false; }
// Ignore paste, drop and keypress operations
el.onpaste = el.ondrop = el.onkeypress = setIgnore;
function setIgnore (e) {
ignore = true;
}
})();
This isn't a perfect solution, however. For instance, the event will still fire for Undo/Redo actions (and, perhaps some other actions) that aren't initiated by the keyboard.
I need to set up an <input type="text" /> so that it will accept only numeric chars, backspace, delete, enter, tabs and arrows.
There's a lot of exemple around there, i started with something similar to this:
function isNumericKeyCode (keyCode){
return ( (keyCode >= 48 && keyCode <= 57) //standard keyboard
||(keyCode >= 96 && keyCode <= 105)) //Numpad
}
$('#myTextBox').keydown(function(e){
var handled = true;
var keyCode = e.keyCode;
switch(keyCode){
//Enter and arrows
case 13:
case 37:
case 38:
case 39:
case 40:
doSomethingSpecialsWithThesesKeys();
break;
default:
handled = false;
break;
}
if ( !handled
&& keyCode !== 8 //backspace
&& keyCode !== 9 //tab
&& keyCode !== 46 //del
&& !isNumericKeyCode(keyCode)){
handled = true;
}
return handled;
});
All that worked perfectly until I hit the "#" key. In my french canadian keyboard, the "#" has his own key (no shift implied) that returns keyCode 51, the same as the number "3".
I think that in US keyboard, the "#" is obtained by pressing shift+3, that may be why they have the same keycode.
Now I realize that I have to handle the shift and alt keys too, but that's another story.
It works differently with the jquery keypress event, which offer the charCode property, but I did not used it at first because of what the documentation says :
as the keypress event isn't covered by any official specification, the
actual behavior encountered when using it may differ across browsers,
browser versions, and platforms.
Also, I would need a workaround in that case to handle tabs, arrows and other special keys since they don't provide a charCode.
So the question is :
is there a way to allow only some specifics chars using the keydown event? And that, in a way that will work independently of the keyboard layout?
As a side quest : Which browsers may be problematics with the keypress event? I mean, currently I don't really care if my website does not support IE6. I am targetting recent browsers.
Edit
As someone pointed out in the comments, this method does not allow user to "ctrl+v" a number in the input. In my particular case this is really not a requirement to be able to paste a number. But this popped something in my head, the user still can right-clic > copy some text in the input, and in that case that could be anything. The more I think of it, the more it seems to me that I will need the keydown event to handle tabs and arrows, and another event to handle the input itself.
Edit2
A lot of beautiful answers here, but the award goes to mrtsherman for the use of input and propertychange events. I will use a combination of this answer for the numeric validation, plus the keyCode event as before for the special use of arrows, tabs and enter keys.
How about something like this. This should cover cut/paste and also rmb content. We monitor the textbox for any change in content. Then we use a regex to filter out characters based on a whitelist. This won't handle non-character key, but I think that is okay.
The \d flag says that only digits should be accepted.
http://jsfiddle.net/UXeva/1
$('#myTextBox').bind('input propertychange', function() {
var text = $(this).val();
if (isNaN(text)) {
$(this).val(text.replace(/[^\d]/gi, ''));
}
});
We bind to two events here. input for FireFox and propertychange for other browsers.
If older browsers are'nt an issue, the number input type should cover this.
<input type="number" />
If not you could use the isNaN javascript function
$("#number1").on('keyup', function() {
var myval = $(this).val();
if (isNaN(myval)) {
alert('numbers only!');
}
});
Personally I would do some regex filtering with a check to see if the value has changed, that will allow any character that does not change the value, like tabs, enter and arrows. With a regex you could also add or remove any character, or you could use \d for digits only or as below, [0-9]. Up to you really what your exact needs are?
var P;
$("#number2").on('keyup', function() {
var V = $(this).val();
if (V != P) {
$(this).val(V.replace(/[^0-9]/g,''));
}
P=V;
});
They could also be combined to something like this:
$("#number3").on('keyup', function() {
var V = $(this).val();
if (isNaN(V)) {
$(this).val(V.replace(/[^0-9]/g,''));
}
});
Here's a FIDDLE to test them out!
Why not do something like this? It uses a combination of the keyup() event and isNaN(). It'll work whether the user types with the keyboard or pastes a number.
The way it works is, as soon as the text changes, it will check if the value is a number. If not, it will trim the input until it is a number. So, if you enter 25s or 25ss, you will be left with 25.
This will work with ctrl+v paste as well. It won't work with right-click paste and for that reason, I have disabled right-clicking only on the textbox.
Live Demo
The Jquery
$(document).ready(function(){
$('#number').keyup(function(){
var input = this.value;
while (isNaN(input))
{
input = input.substring(0,input.length-1);
$('#number').val(input);
}
});
$('#number').bind("contextmenu",function(e){
return false;
});
});
jQuery also provides a shiftkey boolean on the event object:
$('#myTextBox').keydown(function(e){
if(e.keyCode === 51 && !e.shiftKey){
// '3' key pressed while shift was **not** held down (not '#')
}
});
EDIT I reread your question and changed the code above for !shiftkey
I want to capture user's activities on my textbox. Since a normal textbox won't give me enough information on what my user is doing currently with it, I want to start on a custom HTML text box.
For e.g.
If my user is typing
Hello world! (Say he made a typo...) I should be able to tell him that,
H e l l o w o r l e [bksp] d !
also if a user selects a text, I should be notified about it.
P.S. I've mentioned a custom text box inorder to be generic. If I can make use of / create something like a plugin on the already available text box or say even a javascript, it's fine.
Your best bet would be to add functionality to the existing <input type="text"> using javascript.
I don't know how you would create your own textbox as browsers just interpret html which only contain the predefined elements in the HTML specification (apart from certain exceptions such as ActiveX).
As a solution regarding to what you want you can capture every keypress using the onKeyUp event of your document. You can catch every keypress and display them to your liking.
small example:
<script type="text/javascript">
document.onkeyup = KeyCheck;
function KeyCheck()
{
var keyID = event.keyCode;
var keypressed;
switch(keyID)
{
case 16:
keypressed = "Shift";
break;
case 17:
keypressed = "Ctrl";
break;
case 18:
keypressed = "Alt";
break;
case 19:
keypressed = "Pause";
break;
case 37:
keypressed = "Arrow Left";
break;
case 38:
keypressed = "Arrow Up";
break;
case 39:
keypressed = "Arrow Right";
break;
case 40:
keypressed = "Arrow Down";
break;
}
document.write(keypressed);
}
</script>
for a list of all the keycodes see here.
[Update]
I just saw that you are also want to know when someone selects text and luckily for you there are also events that handle this:
An <INPUT TYPE = "text"> aswell as a <TEXTAREA> have an .onSelect event which you can capture. Then you can get the selected text using the method you find on this other StackOverflow Question: How to get selected text from textbox control with javascript
If you are working heavily in javascript I suggest you take a look at JQuery (if you haven't already). It will definitely make your life easier.
I need to change in a text input the character '.' to ',' while typing.
In IE I change the keyCode event property in the keypress event, like this
document.getElementById('mytext').onkeypress =
function (evt) {
var e = evt || window.event;
if (e.keyCode && e.keyCode==46)
e.keyCode = 44;
else if (e.which && e.which==46) {
e.which = 44;
}
};
but it seemes that in Firefox it's impossible to change characters typed in key events.
Any suggestions?
Try this. It works on all browsers:
window.onload = function () {
var input = document.getElementById("mytext");
input.onkeypress = function () {
var evt = arguments[0] || event;
var char = String.fromCharCode(evt.which || evt.keyCode);
// Is it a period?
if (char == ".") {
// Replace it with a comma
input.value += ",";
// Cancel the original event
evt.cancelBubble = true;
return false;
}
}
};
Update: Pier Luigi pointed out a problem with the above. It doesn't take care of the caret position not being at the end of the text. It will append the command to the end even if you're inserting some text to the value.
The solution would be, instead of appending a comma, to simulate a keypress event for the comma key. Unfortunately the way dispatching of synthetic events work in different browsers seems to show a lot of variety and isn't an easy feat. I'll see if I can find a nice and generic method for it.
Assume that all properties in an Event object are immutable. The DOM spec doesn't address what happens when you change those values manually.
Here's the logic you need: listen for all key events. If it's a period, suppress the event, and manually add the comma at the cursor position. (Here's a code snippet for inserting arbitrary text at the cursor position.)
You'd suppress the event in Firefox by calling event.preventDefault(); this tells the browser not to go ahead with the default action associated with this event (in this case, typing the character). You'd suppress the event in IE by setting event.returnValue to false.
If it's not a period, return early from your handler.
Technically you just want to replace all dots with commas.
document.getElementById('mytext').onkeyup = function(){
this.value = this.value.replace('.', ',');
}
If I look at the official Document Object Model Events document, mouse events fields are defined as read-only. Keyboard events are not defined there, I suppose Mozilla followed this policy for them.
So basically, unless there is some smart trick, you cannot alter an event the way you want. You probably have to intercept the key and insert the char (raw or translated) where the caret is, the way JS HTML editors do.
Does this really need to be done on the fly? If you are collecting the information to be posted to a form or submitted to a database, would it not be better to modify the data once it was submitted? That way the user never sees the confusing change.
This is possible now by intercepting and cancelling the default keydown event and using HTMLInputElement.setRangeText to insert your desired character. This would look something like this:
document.addEventListener('keydown', $event => {
if($event.code === 'Period'){
$event.preventDefault();
let inputEl = document.querySelector("#my-input");
inputEl.setRangeText(
',',
inputEl.selectionStart,
inputEl.selectionEnd,
"end"
);
}
})
setRangeText will insert text at the cursor position in a given input. The "end" string as the last argument sets the cursor to the end of the inserted content.
More info here: https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setRangeText