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' />
Community
  • 1
  • 1
SexyBeast
  • 7,913
  • 28
  • 108
  • 196

2 Answers2

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

MightyPork
  • 18,270
  • 10
  • 79
  • 133
  • Just what I was fearing! I hate timers! Juicy way for memory leaks.. :( – SexyBeast Aug 07 '13 at 22:56
  • Eh right, it stinks, but it's your only choice. Even the jQuery-UI autocomplete does it this way. – MightyPork Aug 07 '13 at 22:57
  • 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! – SexyBeast Aug 07 '13 at 23:27
  • 1
    +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! – jim tollan Jan 12 '14 at 23:56
  • 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: http://jsfiddle.net/5RWbH/29/ – Eric Freese Jul 08 '14 at 21:08
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>