jQuery: Getting new value in keydown handler
Asked Answered
C

2

7

I came across this question: onKeyPress Vs. onKeyUp and onKeyDown, from where I found that keypress is supposed to fire whenever a character is typed into the text input. I am trying to run this following code. It is supposed to make the input's background yellow the moment its text length exceeds 0, or white the moment it is 0. I can't make it work. If I try to do keydown, I have the following problems:

  1. If I just type one character and let go, the background remains white.

  2. If then, I press backspace, thus clearing that one character, it turns yellow (just opposite of what I want!). If I press any other key now (Alt,Shift) it will turn white again. In fact, if instead of Alt or Shift I type a character, it will still remain white, taking us back to the first problem.

  3. If I type press a character key and keep it pressed, the background remains white for the first character, and turns yellow 2nd character onwards.

If I try keyup only, these are the problems (as expected):

  1. The background doesn't change as long as the keys are kept pressed, even when a character is added to the empty input or the entire text deleted.

If I try keypress, I face the same problems as keydown, even though it is supposed to work.

If I bind 3 handlers for keyup, keydown and keypress (God I am desperate!), almost all problems are solved except problem 3 of keydown: if I type press a character key and keep it pressed, the background remains white for the first character, and turns yellow 2nd character onwards.

How do I solve this problem?

JS:

$(document).ready(function() {

    $("input").bind("keydown", function() {
        if ($(this).val().length == 0) {
            $(this).css('background', 'white');
        } else {
            $(this).css('background', 'yellow');
        }
    });

    $("input").bind("keypress", function() {
        if ($(this).val().length == 0) {
            $(this).css('background', 'white');
        } else {
            $(this).css('background', 'yellow');
        }
    });

    $("input").bind("keyup", function() {
        if ($(this).val().length == 0) {
            $(this).css('background', 'white');
        } else {
            $(this).css('background', 'yellow');
        }
    });

});

HTML:

<input type='text' />
Casimir answered 7/8, 2013 at 22:15 Comment(0)
W
11

When the keydown event is fired, the character is not yet written to the input box.

I did some research and it's recommended to use timeout in order to get the behaviour you want. Apparently, during the tiny delay, a change event on the input is fired, and .val() then returns the new content.

Here's a working code:

$(document).ready(function () {
    var field = $("input");

    field.on("keydown", function (e) {
        setTimeout(function () {
            if (field.val().length == 0) {
                field.css('background', 'white');
            } else {
                field.css('background', 'yellow');
            }
        }, 1);
    });
});

Accompanied by a jsFiddle

Wellthoughtof answered 7/8, 2013 at 22:38 Comment(5)
Just what I was fearing! I hate timers! Juicy way for memory leaks.. :(Casimir
Eh right, it stinks, but it's your only choice. Even the jQuery-UI autocomplete does it this way.Wellthoughtof
Oh they do? He he, then I guess there really is no workaround! If John Resig can't find a workaround, I am fine with this.. :) Thanks a lot!Casimir
+1 -this almost drove me mad for 2 hours tonite!! then i realised that there must be some klunkiness with the event handler on keydown. appreciated and kudos!Wheelhorse
I was having trouble on iOS Safari with the value of the input reliably being set by the time the setTimeout callback was called. Upping the delay time from 1ms to 10ms did the trick, but seemed to hacky. I tried listening for a change event, but it's not triggered until the input loses focus. It does, however, fire off a keyup event after the value has been set. I am using field.one('keyup', function() { ... }); instead of the setTimeout above, and it seems to be working fine. Fiddle here: jsfiddle.net/5RWbH/29Imparity
A
2

Although it is an old post, I came by the same question. The best way to achieve this without a timer is with input event:

$('#inputText').on('input', function() {
      const inputText = $(this).val()
      console.log(inputText)
  })
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="form-group">
  <input type="text" class="form-control" id="inputText">
</div>
Aceous answered 14/8, 2021 at 21:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.