Inspect the fixed example
The fixed script assigns empty text, 0 and 2.5 in order. The read-only control keeps the sample inputs fixed while still allowing the script to assign its value.
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<label for="size">Example size</label><input id="size" type="number" readonly><output id="result"></output>
<script>const input=document.querySelector('#size');const states=[];for(const value of ['', '0', '2.5']){input.value=value;states.push({value:input.value,asNumber:Number.isNaN(input.valueAsNumber)?'NaN':input.valueAsNumber,numberCoercion:Number(input.value)});}const result=states;
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>[{"value":"","asNumber":"NaN","numberCoercion":0},{"value":"0","asNumber":0,"numberCoercion":0},{"value":"2.5","asNumber":2.5,"numberCoercion":2.5}]| Inspection | Supplied input or action | Result |
|---|---|---|
| Empty | value: "" | valueAsNumber: NaN; Number: 0 |
| Explicit zero | value: "0" | 0 / 0 |
| Fraction | value: "2.5" | 2.5 / 2.5 |
Follow the native mechanism
The raw value is a string. valueAsNumber is NaN when this empty number control cannot represent a number, while Number("") produces zero. The transcript labels NaN explicitly. This compares native API values, not arbitrary localized number parsing or a business default policy.
Compare the material alternative
Coercing raw value with Number collapses empty and explicit zero to the same number. A numeric equality check alone cannot recover the missing input.
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<label for="size">Example size</label><input id="size" type="number" readonly><output id="result"></output>
<script>const input=document.querySelector('#size');input.value='';const blank=Number(input.value);input.value='0';const zero=Number(input.value);const result={blank,zero,same:blank===zero};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>{"blank":0,"zero":0,"same":true}Work the boundary
If this native number control is assigned the fixed text "not-a-number", what remains in value?
Show the worked answer
The native value setter sanitizes it to empty text, and valueAsNumber is NaN. Inspect the real input; Number("not-a-number") alone would not demonstrate that sanitization.
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<label for="size">Example size</label><input id="size" type="number" readonly><output id="result"></output>
<script>const input=document.querySelector('#size');input.value='not-a-number';const result={value:input.value,asNumber:Number.isNaN(input.valueAsNumber)?'NaN':input.valueAsNumber};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>{"value":"","asNumber":"NaN"}