Kalle Richter
Kalle Richter

Reputation: 8768

Why does my HTML number input get stuck with step 0.010000000000000009?

When I create an HTML <input type="number"/> with step="0.010000000000000009". I can only click the button to increase the value once to fill in the min value and then only once for the first increase by the step value, then it gets stuck at this value and further clicks have no effect. I can only reproduce this with min="1.2" max="1.3".

All other combinations of min, max and step I tried allow to click the button multiple times until it reaches max in the defined steps.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>input with decimal step</title>
  </head>
  <body>
    <input type="number" min="1.2" max="1.3" step="0.010000000000000009"/>
  </body>
</html>

Upvotes: 2

Views: 760

Answers (1)

Dai
Dai

Reputation: 155438

I believe that, according to the HTML5 Living Specification, your <input /> element "suffers from step-mismatch":

Following the spec w.r.t. your case...

  • The step value is 0.010000000000000009 (from parseFloat).

  • The step-scale-factor value is always 1 for <input type="number" />

  • The step-base is the min attribute, which is 1.2.

  • The allowed-value-step is step * step-scale-factor which is 1 * 0.010000000000000009 === 0.010000000000000009.

  • Re: "Constraint validation", the spec says this:

    Constraint validation: When the element has an allowed-value-step, and the result of applying the-algorithm-to-convert-a-string-to-a-number to the string given by the element's value="" is a number, and that number subtracted from the step-base is not an integral multiple of the allowed-value-step, the element is suffering from a step mismatch.

    I'll rephrase that to this...:

    • If element has allowed-value-step and numeric value="" attribute.
      • (So this is only after the <input/> is non-empty, such as clicking the up/down buttons once to give it a value of 1.2)
      • ...then subtract 1.2 (the step-base) from 1.2 (the value="") to give 0.
      • And 0 is not an integral multiple of 0.010000000000000009.
      • Therefore the element is suffering from a step mismatch.

Now, w.r.t. your findings:

I can only click the button to increase the value once to fill in the min value

Yes, that follows the HTML5 spec: the initial value="" is empty, so browsers will choose the next valid highest value which is the min="1.2" value.

and then only once for the first increase by the step value

Yes, that's because the next step is 1.2 + 0.010000000000000009 which is 1.210000000000000009 - which both browsers truncate the display to 1.21, however as soon as this happens we see a difference in browser behaviour:

  • Firefox seems to correctly comply with the spec and it sets HTMLInputElement.validity.stepMismatch to true and disables the up/down buttons due to the step-mismatch condition.
  • Chrome 92 does not and using the up/down buttons to set other values still results in HTMLInputElement.validity.stepMismatch === false.

Here's a snippet that shows validity information:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>input with decimal step</title>
  </head>
  <body>
    <input id="inp" type="number" min="1.2" max="1.3" step="0.010000000000000009" />

    <ul id="eventsList"></ul>

    <script>
    const inp = document.getElementById('inp');
    const ul  = document.getElementById('eventsList');

    inp.addEventListener( 'input', logInfo );
    inp.addEventListener( 'change', logInfo );
    inp.addEventListener( 'click', logInfo );

    function logInfo( e ) {
        const li = document.createElement('li');
        li.textContent = e.type + ". Value: " + inp.value + ", valid: " + inp.validity.valid + ", stepMismatch: " + inp.validity.stepMismatch;
        ul.appendChild( li );
    }
    </script>

  </body>
</html>

  • In Chrome, it lets me click the up button 10 times (1.2, 1.21, 1.22, 1.23, ..., 1.28, to 1.29) before it stops, though it never reports stepIsmatch: true.
  • In Firefox, it lets me click the up button 1 times (1.2 and 1.21) before it sets stepMismatch: true.

My money's on Chrome doing their own thing given their interpretation (or at least, their implementation) of the spec is more forgiving and is less likely to result in end-user-frustration when step is set to a strange value.


What's interesting is that the Number type in browsers seems able to accurately represent 0.010000000000000009 without any tell-tale IEEE 754 rounding (ditto Python), whereas C#/.NET's Single (32-bit) and Double (64-bit) floating-point types both choke and give me rather bad approximations.

Upvotes: 4

Related Questions