I have an input.onkeydown
handler and I check input.value
after setTimeout(..0)
.
I expect input.value
to have the new value when the setTimeout
callback runs.
In all browsers except Firefox it does. In Firefox it's not always the case.
The code to check is:
<input id="input">
<script>
input.onkeydown = function() {
setTimeout(() => this.value = this.value.toUpperCase());
};
</script>
The demo: http://plnkr.co/edit/rZmiHdttSXNdpKkR8YbH?p=preview
As I uppercase the input value after setTimeout(..0)
, it should be uppercased always. But as said, in Firefox it's not.
Here's the demo video; the first few seconds demonstrate the problem: https://jmp.sh/9XSROQ2
The relevant spec part is https://dom.spec.whatwg.org/#concept-event-dispatch.
Am I not getting something, or is this a long-standing bug in Firefox?
P.S. If I add console.log
in setTimeout
, I sometimes see the old value.
P.P.S. The purpose of this question is to know if I understand setTimeout
correctly or not. I'm familiar with a variety of ways to uppercase input
; please do not suggest oninput
, requestAnimationFrame
, or such.
Invoking setTimeout with a callback, and zero as the second argument will schedule the callback to be run asynchronously, after the shortest possible delay - which will be around 10ms when the tab has focus and the JavaScript thread of execution is not busy.
You can also prevent the setTimeout() method from executing the function by using the clearTimeout() method. If you have multiple setTimeout() methods, then you need to save the IDs returned by each method call and then call clearTimeout() method as many times as needed to clear them all.
If you call setTimeout() with a time of 0 ms, the function you specify is not invoked right away. Instead, it is placed on a queue to be invoked “as soon as possible” after any currently pending event handlers finish running.
Depending on the way you use firefox, either may be helpful. If you want to extend your timeout, type about:config in your search bar on the top. From there it will take you to a list of preferences. There is a search bar. Type "Timeout" into it. The top two are disable timeout and the "count timeout".
Mozilla's docs expand that to cover repeated, not just nested cases (so setInterval with interval of 0 will reschedule immediately a few times, then delay longer after that), but simple uses of setTimeout with minimal nesting are allowed to immediately queue.
Javascript is single threaded application so that don't allow to run function concurrently so to achieve this event loops are use. So exactly what setTimeout (fn, 0) do that its pussed into task quest which is executed when your call stack is empty.
Historically browsers sets this minimum to 10 milliseconds, but the HTML5 specs and modern browsers have it set at 4 milliseconds. If nesting level is greater than 5, and timeout is less than 4, then increase timeout to 4. To implement a 0 ms timeout in a modern browser, you can use window.postMessage () as described here.
The actual specs are here and actually do not define the relation between the keydown and the change of the input's current value.
However they read, (emphasize mine)
For input elements without a defined input activation behavior, but to which these events apply, any time the user causes the element's value to change without an explicit commit action, the user agent must queue a task to fire an event named input at the input element, with the bubbles attribute initialized to true. The corresponding change event, if any, will be fired when the control loses focus.
So it is actually specced that the input
event (that you should be listening to anyway) must fire asynchronously. Since this event is the one that testifies of a change in the current value, I don't think Firefox's behavior of applying the changes caused by the Key event in the next event loop is hardly a bug; remember that browsers have to make this change asynchronously after the event is dead (and can't be cancelled anymore by any handler).
Some additional notes (which may be related to a real cause btw), inputing a combined character (e.g ^
+ a
=> â
) I have a 100% repro in FF on macOs (because yes, I suspect it may also be related to the OS).
But of course, even if it doesn't go against any specs, and even if you have an easy fix (listen for input event) you may still want to file a bug-report at least for not behaving like other vendors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With