I was wondering if it were possible to get the end result in the keypress event?
Currently, I am using the keyup because it is activated after the user has done text editing in a texteara, but I have written a method that does something similar using the Mootools library:
input.addEvent("keypress", function (input) {
var previous_result = this.value;
var end_result = this.value + input.key;
});
However, this method is horrible when dealing with special keys such as backspace, or if the user chooses to use CTRL + a && Backspace in which case the value of the input element would not be "an empty string".
I'm curious because I have observed Google's search engine sending XMLHttpRequest AND mutating the page before the keyup event triggered.
Additionally, the input they use manages to overcome my problem of removing entire text while still enjoying the luxury of keypress.
This will make it work:
input.addEvent( "keypress", function ( input ) {
setTimeout(function () {
input.value // returns the updated value
}, 0 );
});
Live demo: http://jsfiddle.net/yQQ5P/ (I use built-in API, as I don't know Mootools)
So, you use a zero-timeout which acts as a yield. This gives the browser's UI thread an opportunity to updated the input's value.
Related
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();
});
}
How do I prevent the user from changing a string in an input box if the combined value of that input with their latest keypress does not pass a regular expression?
I see all kinds of examples on how to use keypress for testing individual characters, but I need to match the entire value to a regular expression, not just the key they pressed.
For example, The textbox needs to conform to the following regular expression:
"^023-[0-9]{0,7}$"
So if they want to change "023" to "23", it should stop them when they delete the 0 character. The same is true for deleting the - after 023. It should also stop them once they enter more than 7 numbers after 023. This cannot be done on blur. It must be done after every key stroke. Performance is not an issue.
If I use keypress() event in jQuery, and get the value of the input element like this:
$(this).val()
Then I will only get the value before they pressed the key - not after. Thus, there is no way to test the input against my regular expression. I cannot simply append the key pressed to this string, because I cannot make the assumption that they are only entering keys at the right-most side of the string.
I have looked into keydown/keyup events, and while keyup seems to give me the current value of the input after the user has pressed a key, I am finding it difficult to remove the effects of what they typed... which keypress() does not have a problem with apparently.
var regex = new RegExp("^023-[0-9]{0,7}$");
$("#number").keyup(function(event) {
var number = $(this).val();
if(!regex.test(number)) {
event.preventDefault();
return false;
}
});
The above code just doesn't work. It seems keypress() gives me the ability to stop what they typed, but keyup gives me the ability to get the current value. I need a solution that does both ;)
The problem really stems from the fact that the browser has no MVC architecture. The model is the view. Unfortunately, we can't validate the updated model before the view is updated... because we need the view updated during a keyup event to get the updated model data... and by then, it's too late to prevent them from updating the view since it's already been updated.
How about this:
var prevValue = "";
$("#number").keydown(function(e) {
prevValue = $(this).val();
});
$("#number").keyup(function(e) {
...
if(!regex.test(number))
$(this).val(prevValue);
// show error or other processing you need to do
});
try this:
var pattern = new RegExp("^023-[0-9]{0,7}$");
$("#number").change(function(e){
if (!pattern.test($(this).val())) {
return false
}
})
The event parameter that you have will contain all the details of the event that you are handling. This will include the key that was pressed.
Of note is that keydown and keyup events will return the key pressed, while keypress will return which character is entered which may be better for your choice.
See the help page here: http://api.jquery.com/keypress/ for more details.
In summary though event.which should do what you want. You can combine that with the original value of the textbox and from that get the new value. If it isn't valid then cancel the event, if it is valid then let it go...
If you can use the jQuery Validate plug-in, you can create a custom validator to match your RegEx. Then all you have to do is map the validator to that input. You'll still have to validate on the server-side, but you're probably already doing that.
To do what you want, the jquery caret plugin is required. Please note that you'll need to remove the comments from the beginning of the file (or at least move them) or the byte order mark at the start of the code will cause an error.
So, what you need to do is capture the keypress, insert the character at the correct place in the string, check if that new string is valid and then either show the new insertion or not. I think the following does what you need:
$(document).ready(function(){
var regex = new RegExp("^023-[0-9]{0,7}$");
var caretpos;
$('#number').keypress(function(event){
// get the caret position
caretpos = $(this).caret().start;
//split the current value according to where the caret is
valuestart = $(this).val().substring(0,caretpos);
valueend = $(this).val().substring(caretpos);
// insert the fired character into the string
valuetocheck = valuestart + String.fromCharCode(event.which) + valueend;
// check if the proposed new value passes the regex
if (!regex.test(valuetocheck)) {
// failed, prevent the character being shown
event.preventDefault();
}
});
});
I've tested this in the latest versions of FF, Chrome, Safari, Opera and IE9. In FF and Opera, cursor key presses are completely ignored while they move the caret as usual in the other browsers so you may want to add some code to ensure consistent behaviour for that. You may also want to add some code to handle what will happen if the user selects some characters rather than just clicking into the input. The jquery caret plugin has usage examples for selections.
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 found this question. However, most of the answers intercept the key press, check the key code, and stop the event if it isn't an acceptable key code.
There are a some problems with that.
A common bug among all the answers is that shift key combinations are not prevented so users can enter stuff like "#" or "&".
Users can still paste in bad values from the clipboard.
When you start messing around with the key press event, the webpage starts doing weird, unexpected things. For example if the field has focus and the user tries to reload the page by pressing command-R it won't work. Or they can't tab out of the field.
I would like a solution that allows the keypress, then checks the entered value against a regex and removes unwanted characters.
I'm looking for a solution along these lines:
Doesn't intercept the keypress event.
Ideally uses a regex after the keypress.
OK if it uses jQuery.
Doesn't require installing a plug-in.
I tried this function:
$('input.numerals_only').keypress(function() {
var v = $(this).val();
var no_nonnumerals = v.replace( /[^0-9]/, '' );
$(this).val(no_nonnumerals);
});
But that doesn't work. I'm still able to type in letters.
Any info on why it doesn't work? How could I modify it so it works? Other suggestions?
If you use keyup and also blur you should handle most cases. Someone keys in text and also if they paste in a value into the text box. As the blur function will remove the bad values as well.
$('input.numerals_only').keyup(AllowOnlyNumber).blur(AllowOnlyNumber);
function AllowOnlyNumber()
{
var v = $(this).val();
var no_nonnumerals = v.replace(/[^0-9]/g, '');
$(this).val(no_nonnumerals);
}
Of course if html5 is an option you could always do:
<input type="number"/>
jsfiddle example.
The thing your regexp is missing, which is why it didn’t work for keypresses neither, is the g flag for a global regexp check.
Use var no_nonnumerals = v.replace( /[^0-9]/g, '' );
If you use keyup instead of keypress it will always remove the incorrect characters.
You can also use this to actually block the events:
$('input.numerals_only').keypress(function(event) {
if(event.keyCode < 48 || event.keyCode > 57){
return false;
}
});
Use the keyup event. It is triggered after the key is processed by the browser:
$('#txtInput')
.keyup(function(ev) {
var newValue = this.value.replace(/[^0-9]/, '');
$(this).val(newValue);
});
I wrote a jQuery plugin to do this. Demo and code here http://brianjaeger.com/process.php
It uses a regular expression to validate whatever you want.
There are a few issues:
it's a plugin (however, you could suck out the code and plunk it into your other JavaScript if you want. -its easier just to use as plugin)
Unfortunately there is no way to limit pasted information directly (across all browsers) - My solution to this was to add validation on focus change as well as keypress.
I am creating a smart textarea that needs to recognise a term typed after the '#' symbol.This needs to be able to work for the term CURRENTLY being typed and be able to work for multiple instances of the '#' symbol in a single textarea.
It is designed to work the same way as Facebook when you type the '#' symbol to tag a person in a post. A drop down list will appear and list items will get filtered depending on the term after the '#' symbol.
This also needs to work if the user were to type a term then amend it later. I understand this complicates things a little.
What is the best way to achieve this functionality?
I don't know if it helps but here's i small script to find the hashes.
http://jsfiddle.net/aNgVV/
I suggest you look at the jQuery UI demo for the Autocomplete widget, specifically the demo for using a remote datasource with cache. Specifically for the following reasons:
It automatically takes care of the drop-down widget you mentioned.
It demonstrates how you can populate that drop-down with items based on an AJAX call (which I presume you need).
The demo for Autocomplete caching parses the text in the INPUT element, as it tries to determine whether or not the value the user is currently typing has already been cached, and reacts accordingly. I assume you can do something similar to check for the # types, and to check if a previous # tag is being modified as well.
Initially you need to catch the '#' key being pressed and then capture the subsequent key presses and pass them to a function to handle your auto completion requirements. A rough outline to of the code is below. You may need to catch whitespace key presses as well to stop the auto-completion.
var hashKeyPressed = false;
$('#TextArea').keyup(function(event) {
if(event.keyCode == '222') {
// this will catch the '#' key
hashKeyPressed = true;
}
if(hashKeyPressed) {
// Here you can start build up subsequent key presses into a string
// and pass them to a function to handle the auto-completion
}
});
You can capture the keyup event and check what has been entered last like so:
$('#myTextArea').keyup(function () {
var len = $(this).val().length;
if ($(this).val().substring(length - 1, 1) == '#') {
// Do whatever you want to do here
}
});
EDIT:
You are right - you could do it this way instead:
$('#myTextArea').keypress(function (e) {
if (e.which == 222) {
// do something here
}
});