5

I can only retrieve the value without the newly pressed key. Using the keyup event isn't an option, because it does not fire if the user doesn't release the key. This is important because I want to act upon every single keypress.

Combining the old value with the keyCode that is reachable from the event's arguments isn't acceptable either, because it's not guaranteed that the user will type to the end of the string in the textbox.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Attila Kun
  • 2,215
  • 2
  • 23
  • 33

4 Answers4

7

Wrap your handler code in setTimeout(function() { ... }, 0).

This will execute the code in the next message loop, after the value has been updated.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • But then I would lose the opportunity to prevent the event. However if I combine this with Jeff T's suggestion, I could rollback the input's value to its previous state if I find it invalid. I'm still open to other ideas though. – Attila Kun Aug 11 '10 at 23:25
  • If you want to be able to rollback to the previous state, you could save the value before entering the setTimeout, and refer back to it if you discover the need to rollback. Updated jsFiddle here: http://jsfiddle.net/3tRMx/1/ Try entering 'xx' to see the rollback at work. – Ender Aug 11 '10 at 23:35
  • @Ender: I modified your code and discovered an interesting issue: If you keep a key pressed continuously for long enough some keys eventually "escape", so they will appear regardless that they are supposed to be deleted in the next message loop. I guess this happens because the next keypress event happens before the previous keypress' setTimeout callback. Related example: http://jsfiddle.net/vrjAA/ (hold the letter "i" pressed for some time) – Attila Kun Aug 11 '10 at 23:43
  • @kahoon - Ha! Right you are. After a bit of thinking, I came up with something which I think solves the problem. Turn oldVal into a global (or at least semi-global) variable, and update it in the keydown event instead of the start of keypress. Using this, I held a key down for at least a minute with no "escapees." Modified your jsFiddle here: http://jsfiddle.net/vrjAA/1/ – Ender Aug 11 '10 at 23:53
2

It's possible to work out what the value will be after the keypress. It's easy in non-IE browsers and trickier in IE, but the following will do it:

document.getElementById("your_input").onkeypress = function(evt) {
    var val = this.value;
    evt = evt || window.event;
    var charCode = typeof evt.which == "number" ? evt.which : evt.keyCode;
    if (charCode) {
        var keyChar = String.fromCharCode(charCode);
        var start, end;
        if (typeof this.selectionStart == "number" && typeof this.selectionEnd == "number") {
            start = this.selectionStart;
            end = this.selectionEnd;
        } else if (document.selection && document.selection.createRange) {
            // For IE up to version 8
            var selectionRange = document.selection.createRange();
            var textInputRange = this.createTextRange();
            var precedingRange = this.createTextRange();
            var bookmark = selectionRange.getBookmark();
            textInputRange.moveToBookmark(bookmark);
            precedingRange.setEndPoint("EndToStart", textInputRange);
            start = precedingRange.text.length;
            end = start + selectionRange.text.length;
        }
        var newValue = val.slice(0, start) + keyChar + val.slice(end);
        alert(newValue);
    }
};
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • Thank you, this is the closest to what I wanted to achieve. One major issue though: it still doesn't protect from copy and pasting arbitrary data into the input. – Attila Kun Aug 26 '10 at 21:22
  • OK. You didn't mention that in the question. In most browsers you can prevent that with a simple `document.getElementById("your_input").onpaste = function() { return false; };`. Firefox 2 and I think all version of Opera don't support the paste event. – Tim Down Aug 26 '10 at 23:42
  • Nice. I modified this slightly to all me to set it up as a standalone function that takes event as an argument and returns the expected new value, but I guess stackoverflow won't let me paste it here . . . – Robert MacGrogan Aug 29 '12 at 22:11
  • @RobertMacGrogan: You can link to a jsFiddle or similar from the comments. – Tim Down Sep 07 '12 at 14:21
1

Here's an idea that might work. Use keypress and store the val at that point so you can always compare the current value to the last value and find the difference in the strings. The difference will be the key that was pressed.

One way to do this would be to turn the strings into arrays and compare the 2 arrays like they are doing here: JavaScript array difference

Never tried anything like this so it might not be viable but might be worth a shot.

Community
  • 1
  • 1
Jeff Treuting
  • 13,910
  • 8
  • 36
  • 47
0

I had a specific case with TAB key, which changes the e.target in keyUp, so that's solution - bind to container element, grab target input in keyDown handler, subscribe to keyUp and read the value.

$("#container").keydown(function (e) {
   //here you decide whether to handle the key event, and grab the control that sent the event 
   var myInput = e.target;
   $("#container").one('keyup', function() {
      console.log(myInput.val());  
      // do smth here
   });
});
Kosau
  • 1,373
  • 12
  • 12