Background:
I am writing a script that does some stuff when a user clears an input type="search" (essentially the same as type=text) using backspace or delete. I want to address the case where user highlights original text and starts typing new text, and also pasting.
Because of this I cannot use keydown (fires before input value is changed). Cannot use keypress (need to fire when backspace/delete is pressed, and it fires too early as well). Using keyup is bad because I can't clear when value === 1 (might already be several characters in the field). I can build in some slop but then it won't clear right away which looks buggy.
Question:
So the DOM input event fires right when the value is updated, which is exactly what I want (tested using jQuery on 'input'). However, I cannot find the captured key (which I need to differentiate between deleting and entering content). I couldn't find great info on the spec. It seems like this is going to be implemented in the data section of the event, which does not yet have any browser support. Does that mean it is currently impossible to get the key from the oninput event? If so, how do most developers handle this? I imagine wanting the key immediately after it is entered is very common, surely there is a decent solution for this?
Current implementation for the curious:
For the time being I am grabbing the value right after keydown by using a setTimeout of 0. This gets me both the key and the updated value, but feels dirty and requires extra handlers for onpaste and such. While writing this it occurs to me I could probably use oninput by keeping track of the last value and comparing to current to differentiate between entering and deleting, but that doesn't seem all that much better, and would still require a separate paste handler since I want my event to fire every time a user pastes, and it doesn't seem to have any flags that would let me know the event was a paste.
I decided the last suggestion of dandavis was the best workaround. I used keydown to grab key info and triggered the action on paste, empty field, or 1 character and last character was not backspace/delete (on input does not fire on enter, modifier keys, etc. so those don't need to be accounted for).
The code looks like this:
var onClear = function(action){
var lastKey;
jQueryObject.keydown(function(e){
lastKey = e.which;
});
jQueryObject.on('input', function(){
if( !this.value.length || (this.value.length === 1 && lastKey !== 46 && lastKey !== 8) ){
action();
}
});
jQueryObject.on('paste', function(){
action();
});
}
Related
I am working on a WYSIWYG-like editor for equations (in combination with plain text).
Since implementing the functionalities (for equations) that I'd like to have won't work using the existing frameworks (contentEditable or document.designMode), I am now building an editor from scratch. (so far it has worked out good, I've successfully implemented most functionalities of a normal editor)
I've been using the keydown event to detect user input while in "edit-mode" (that is, when the user has clicked on the editor-area, also displaying the cursor), but the problem with that is, that when clicking "alt" or "strg" or other keys that aren't actual characters, they'll also get displayed in my editor.
Now, what I've tried is to ignore those keys by using if-statements, but there are 2 issues I see with that:
1. It may influence performance, when too many keys have to be ignored
2. I can never be sure, if there doesn't exist some exotic key, perhaps on a Mac or so, which I didn't ignore
I have also tried to use the keypress event instead, which worked mostly fine, but still displayed "Enter" and "Delete", so who knows what others keys it may display too. Also, it is labelled as "deprecated".
Are there any better ways of doing that, or will I just have to make a big list of keys to ignore?
A simple (but limited) approach would be to check if the keydown event's keyCode is between 65 (code for key 'a') and 90 (code for 'z').
If you need more than just letters though, another solution would be to check the event's key and its length. For actual characters, the key simply holds that character (so length 1). For Ctrl, Shift, etc., key will hold the full name of the key, e.g. "Control", "Shift", etc.
So, if a key is an actual character, the key property will have a length of 1.
document.onkeydown = function(e) {
var keycode = e.keyCode;
if(e.key.length == 1) {
document.querySelector("#editor").innerHTML += e.key;
}
}
<p id="editor"></p>
You can check this one too
document.addEventListener("keydown", (event) => {
console.log(event);
if (event.code === "Escape") {
console.log("abc");
}
});
Attempting to log the input that the following gives me. But I am unable to even get anything other then false when attempting to type in the box. As in i'm able to type a single character, have it return true and then almost immediately it choses to no longer respond and always returns false no matter what I type inside of the box.
function search() {
var value = document.getElementById("search").value;
console.log(value == ''); // true
}
<input type="text" onkeydown="search()" id="search"></input>
Testing - Chrome 40.0.2214.94 (64-bit)
Problem is your trying to compare strings. When you do the comparison with a == it is looking at values. You need to use === then you will be able to check if the input is equal to whatever you are searching for.
Here is the updated JSFiddle
You have to use keyup event because you want result after the key is pressed.
Keyboard events is one of wilder parts of frontend development. There are inconsistencies and cross-browser problems.
But still there are recipes which help to cope with ordinary situations easily.
There are following keyboard events:
keydown
A key is pressed down.
keypress
A character key is pressed.
keyup
A key is released.
There is a fundamental difference between keypress and keydown.
Keydown triggers on any key press and gives scan-code.
Keypress triggers after keydown and gives char-code, but it is guaranteed for character keys only.
You need to use keyup, because it triggers after keypress.
<input type="text" onkeyup="search()" id="search"></input>
see KeyEvents
Is it possible with only one bind to either keyup, keypress or keydown to find out that the field value has been changed?
I mean - some key presses don't change the value (like pressing left arrow) and some do (like pressing any letter-button or backspace).
So is it possible to know that key press caused the value change having only one binding?
PS: yes, I realize I could save the value somewhere and compare it in the very begin of event handler, but is there a solution without temporary variables?
I don’t think so. I mean, you can map key codes that you assume will be silent, but that map might not be 100% reliable as the input value can change or loose focus depending on how the OS and browser is set up.
Is there a special reason for not detecting change via a variable? It seems like the most reliable thing since this is also exactly what you need to detect:
var input = $('input'),
val = input.val();
input.keyup(function(e) {
if (val != (val = $(this).val())) {
console.log('change');
}
});
If you don’t want to use stray variables, how about saving it in the data attribute?
$('input').keyup(function(e) {
if ($(this).val() != $(this).data('value')) {
console.log('change');
}
$(this).data('value', $(this).val());
});
Update based on your comment
You can also use the input event (in modern browsers) to detect change if you don’t care about keys:
$('input').bind('input', function() {
console.log('changed');
});
The last option would be to use an interval and keep checking the input field (this might be the most reliable option).
Based on information from MDN's KeyboardEvent section, it looks like that:
all keys you are interested in will reliably generate a keypress event each time the key is about to be processed
you can tell which key is being pressed (keyCode / key), and also if it has a printable representation or not (if it does then it will alter the value; but keys without printable representation may also alter it)
Going to other sources than MDN yields also this nice resource which has information on how the keyCode property is populated across browsers.
A superior alternative to the above would be the DOM level 3 textInput event, which however also has serious drawbacks:
It is not currently implemented across major browsers
It does not, as far as I can see, apply when text is removed from an input element
I'm quite sure you cannot do this properly without storing the original value somewhere.
Try this if you only want to detect the changes really done by keyboard interaction.
$('#foo').keydown(function (e) {
this.data = this.value;
});
$('#foo').keyup(function (e) {
if(this.data != this.value) {
console.log('changed from "' + this.data + '" to "' + this.value + '"');
}
});
Note there still is the cornercase when the user holds a key down (lets say: arrow key), then modifies the field content with the mouse, then releases the key.
But this is very unlikely to happen and i guess is acceptable.
I am trying to edit some existing JavaScript validation code.
Using the onkeydown event, the code checks that the value in a textbox does not go past a maximum length. I cannot use <input maxlength="value" /> as there may be some formatting characters in the string that I can safely exclude from the maximum length.
The code works fine apart from when the user has pressed the insert key to turn overtype on and they have reached the maximum length. When this occurs if they place the cursor before a character and try to overwrite it the validation thinks that this will go over the limit and doesn't realise that a character will actually be replaced.
This answer states that I cannot detect if overtype is on, but doesn't provide any references. So assuming that I cannot detect overtype, is there anyway in the onkeydown event to detect if a character is going to be replaced.
I am happy with an IE only solution.
Update: onblur is not appropriate as this will let them go many characters over the limit before warning them of the maximum length. I would like to prevent them from going over the limit.
Your handler should look at the entire value and check the length. If the length is legal, return. If not, you can update the value with a substring. You may have to use caret position to determine exactly how to manipulate the string which can be tricky as it's slightly different in IE and other browsers.
This is different from what you have now which is probably preventing keypress when max length is reached. Don't prevent keypress, just trim the resulting string.
I don't think your problem is with the onblur validation, but an event you calling on key press by the sounds of it (eg preventing the user to key any more once they reach the limit) or I have misunderstood.
IF your validation is indeed onblur, you shouldn't have to worry about things like insert/overwrite being enabled, you are only interested in what the value of the input element is once the user has completed their input.
IF you are trying to stop the user if they reach this limit as they type, I would write a function to compute the actual length you are testing. For eg,
function validateMyInput() {
var myInputField = document.getElementById('myInput');
var removeAllExcludedCharsResult = myInputField.value.replace('&','');//exclude &
var totalLength = removeAllExcludedCharsResult.length;
if(totalLength < 500) { //limit of this test is 500
return true;
}
else {
return false;
}
}
Obviously change this function to what you need and maybe make it more generic by passing in the element of the input, max length and array of excluded chars to make it reusable.
UPDATE
I have tested this problem is Chrome and the insert key seems to be ignored. IE on the other hand does overkey. However, it seems page specific, for eg, if i have enabled insert on Page A, it doesn't seem to affect Page B. I found this page which seems to be able to grab the keycode event even when insert has been pressed. It might be due to the following code?
if(window.event) {
event = window.event; //where event is the javascript key event.
}
Either way, the link above seems to have accounted for the problem, hopefully it will have the answer if the above is not correct.
Hope I haven't misunderstood what the problem was and this helped.
I'm trying to intercept the command + keystroke in Safari. I've added an event handler as follows:
document.onkeypress = handleKeyPress;
function handleKeyPress(event) {
if ("+" === String.fromCharCode(event.charCode) && event.metaKey) {
// my code here
return false;
}
return true;
}
When I hit command shift = (shift = is + on my US keyboard), the if statement does not return true.
If I remove the event.metaKey portion of the if statement and hit shift =, the if statement does return true.
Also, if I change the matching string from "+" to "=" and hit command = (with or without the shift key), the if statement does return true.
Is there a way to actually detect the command + keypress (without assuming that the + key is shift = and checking for the event.shiftKey, since this will not be true for some non-US keyboards)?
First of all, I can't necessarily recommend using ⌘+ as a shortcut, since it already means something in Safari (namely, zoom the page). Someone might want that to still work normally. Depending on what you're building, it might make sense, but don't override the default shortcuts unless you're sure.
Anyway: Key events are tricky, often because the keyCode/charCode/which properties don't always match up with the right letter, so String.fromCharCode won't always get you a proper string. keyIdentifier is sometimes a better thing to look at, but not always (for instance, its codes for letter keys are always uppercase letters).
What I've done in the past (and I'm not sure that this is the best way to do it, but it works ok), is to instead listen for keydown and keyup events, and "stack" modifier keys. I.e. whenever a key is depressed, check whether it's a modifier key (i.e. if it's cmd, ctrl, alt, or shift). If it is, add it to the "stack" of modifiers. Do the opposite on keyup; if the released key was a modifier, remove it from the stack.
Now, back in the keydown-handler, if the key wasn't a modifier key, you can send the keydown event (which, unlike a keypress event, will have a pretty reliable keyIdentifier property to check), and the stack of modifiers along to some other callback, that'll take it from there.
In your case, such a callback would check that the cmd-key is in the stack, and that the keyIdentifier for the keydown event is "U+002B" (which is the unicode code for +).
I've put together a jsfiddle example of what I'm talking about. If you click in the "Result" pane (to make sure it's got focus), and press ⌘+, it should show the key combo you used, and write "Success!" below. Otherwise, it'll just show the key combo. On my keyboard, the plus sign is directly accessible, so the key combo I see is just "⌘+". But if you need shift to type a plus sign, you should see "⇧⌘+".
It's a generalized piece of code that's good for handling keyboard shortcuts in Safari/Mac, so you can build on it, if you want. You'll want to add a few event listeners to reset the modifier stack on blur events and such. Ideally, the modifier stack would reset automatically, as you release the keys, but since something might cause the browser, or the observed element/window/document, to lose focus, the keyup events won't be handled, and the released keys won't be removed from the stack. So check for blur events.
Register the event object in a window object level one, event = window.EE for example, then browse it with Firebug or Safari’s Web Developer Tools and see the values that are triggered when you want. So you will know what to compare against.
Sadly, I don't think there is an answer. The problem is, for CMD+SHIFT+= the charCode being pressed is 61 (=), not 43 (+). This appears to be a system-wide design, in that Mac OS X itself interprets CMD+SHIFT+= as = plus two modifiers, not + and COMMAND.
I put together a simple jsFiddle to show this: http://jsfiddle.net/ScuDj/1/
Basically, you are going to have to deal with keyboard layouts if you want to detect COMMAND +.
Alternatively, you could only support the numeric keypad + - that works consistently! (just kidding ;-)
(Also: I tried to attach to the textInput event, but couldn't get it to register globally. I think it only works on domNodes. I've not really used that event much.)