keypress event doesn't log input value first time event is fired
Asked Answered
R

5

7

The first time a keypress event fires, it logs an empty input value even though the input has a value. The second time it logs the value but is one keystroke behind in comparison with the input's value. You can check this behavior on the next example:

document.addEventListener('DOMContentLoaded', () =>
{
    const input = document.querySelector('input');

    input.addEventListener('keypress', e =>
    {
        console.log(e.target.value);
    });
});
<input type="text"/>

However, the next workaround makes it work, even though I pass in 0ms.

document.addEventListener('DOMContentLoaded', () =>
{
    const input = document.querySelector('input');

    input.addEventListener('keypress', e =>
    {
        setTimeout(() => console.log(e.target.value), 0);
    });
});
<input type="text"/>

Why is this hapenning?

Rutty answered 22/5, 2019 at 3:30 Comment(1)
For future reference, the keypress event is deprecated and should no longer be used.Cornute
S
12

When you press a key for the first time, the value assigned to the input is empty at the time the keypress event takes place, then the character is added to the input, but a moment later. This same is valid for future keypress events, the value of the input you read is the previous before the input changes. Also, if you read on the MDN there is a warning about keypress being dropped. Hence, and instead, you may want to listen on keyup event as shown on the next example:

const input = document.querySelector('input');

input.addEventListener('keyup', e =>
{
    console.log(e.target.value);
});
.as-console {background-color:black !important; color:lime;}
<input type="text" id="input">
Squall answered 22/5, 2019 at 3:37 Comment(1)
It might be worth mentioning that if what you care about is the fact that the input value changed and not specifically that keys were pressed, there is also an input event for that exact purpose.Cornute
N
2

keypress event dont change input value - you read 'old' value - current key is in e.key

document.addEventListener('DOMContentLoaded', () => {
  const input = document.querySelector('input');
  input.addEventListener('keypress', e => {
    console.log('value',e.target.value);
    console.log('key',e.key);
  });
});
<input>

you can use onkeyup event to have current value in event.target.value

document.addEventListener('DOMContentLoaded', () => {
  const input = document.querySelector('input');
  input.addEventListener('keyup', e => {
    console.log('value', e.target.value);
    console.log('key', e.key);
  });
});
<input>

The input value will be update before setTimeout function will be executed (even for 0 ms) - keyword: js event loop

Narial answered 22/5, 2019 at 3:38 Comment(0)
R
2

According to jQuery docs:

The keypress event is sent to an element when the browser registers keyboard input. This is similar to the keydown event, except that modifier and non-printing keys such as Shift, Esc, and delete trigger keydown events but not keypress events. Other differences between the two events may arise depending on platform and browser.

The keyup event is sent to an element when the user releases a key on the keyboard.

The oninput event it's an event that triggers whenever the input changes.

So the keypress will be fired before the value change, use keyup instead

About the setTimeout, when you put the codes in that place, it will be Asynchronous function (even for 0 ms). In javascript, the Asynchronous function will be executed after all Synchronous commands are finished

Ronironica answered 22/5, 2019 at 3:49 Comment(1)
Even though your answer is correct, you probably shouldn't quote from the jQuery documentation here, as jQuery has its own event system, and the question doesn't involve jQuery at all. Better to refer to MDN for example.Iny
D
0

That's because it's being fired before the input value is updating. You can try using keyUp instead of keyPress.

Dipody answered 22/5, 2019 at 3:37 Comment(0)
H
0

The keyup event fires when the key is released which generates the feeling of a little delay. I'd rather prefer to use the keydown event which have the same behavior as the OP is exposing. (Note that the keypress event is now deprecated).

Instead of waiting for the next tick (by using setTimeout) you can concatenate the current value with the pressed key:

document.addEventListener('DOMContentLoaded', () => {
  const input = document.getElementById('input');

  input.addEventListener('keydown', event => {
    console.log(event.target.value + event.key);
  });
});
<input type="text" id="input">

EDIT: After trying this solution a little more, it fails to work as expected when moving the cursor between the input text or when reaching a maxlength, so after all I ended up using the workaround proposed by the OP with setTimeout and a timeout of 0

Hooge answered 9/6, 2020 at 17:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.