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.