Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this input valid

Using input.validity.valid, why does my number input return valid for the value 0. in this scenario?

<input type="number" min="1" max="999" step="1" value="0." id="one" /> <!--Valid??-->
<input type="number" min="1" max="999" step="1" value="0.0" id="two" /> <!--fine-->
<input type="number" min="1" max="999" step="1" value="0" id="three" /> <!--fine-->

Chrome 37.0.2062.103 m

jsFiddle

Edit:

Oddly, any negative number with a period at the end also breaks the validation:

<input type="number" min="1" max="999" step="1" value="-14." id="one" />

jsFiddle

like image 481
CodingIntrigue Avatar asked Sep 11 '14 12:09

CodingIntrigue


2 Answers

Note: since this answer was written, the Chrome bug mentioned in #1 has been fixed and so now only #2 applies.

There are actually two answers here:

  1. there's a bug in Chrome which means a value like 0. is not validated correctly when you enter/set it in an input field.
  2. if you set an invalid value in the HTML markup, the browser is required to ignore it and set the value to the empty string, which is considered valid unless the field is marked required.

Strictly speaking, it is the first one that is causing the behaviour you are seeing, because it stops #2 from ever happening, but if the bug were fixed in Chrome then as the question stands (i.e. only dealing with the initial markup, not any subsequent user interaction), #2 would apply.

It's also worth noting that #2 is what causes the similar (but not identical) behaviour in Firefox.

1. The Chrome bug

The reason for #1 is that Chrome is using different parsing algorithms in its `rangeUnderflow` detection and its `badInput` detection.

rangeUnderflow uses Decimal::fromString(...) which returns NaN if the input ends in a . causing rangeUnderflow to returnfalse:

if (!numericValue.isFinite())
   return false;

In contrast, badInput uses StringToDoubleConverter::StringToDouble(...) which basically ignores a trailing . so the input is not considered 'bad'.

According to the spec:

A string is a valid floating-point number if it consists of:

  1. Optionally, a U+002D HYPHEN-MINUS character (-).
  2. One or both of the following, in the given order:
  3. A series of one or more ASCII digits.
    1. A single U+002E FULL STOP character (.).
    2. A series of one or more ASCII digits.

Note that 2.2.2 is not optional - i.e if you have a . you must have digits after it.

So it's the badInput that's incorrect. (If Chrome was at least consistent about treating 0. as valid, the rangeUnderflow would then have been detected)

2. Markup vs user input

**But** if/when they fix the bug in Chrome, with the HTML in the question, it will *still* show as valid. Why?

Take for example if you set value="aaa" in the HTML. The input will still show as valid, even though aaa would cause badInput to be true.

This is because while parsing/rendering the page, Chrome runs a value sanitization algorithm on the attribute value. When it sees a value that is not a valid floating point number, it must set the input's value to the empty string as required by the spec. And the empty string is considered valid unless the input is marked required. This currently doesn't occur in Chrome for value="0." because it (incorrectly) passes the value sanitization algorithm. However, if/when they fix the bug in Chrome, this is what you would expect to happen.

If you actually type aaa into the input, it will then show as invalid, i.e. badInput===true. Similarly, in Firefox, and in Chrome if/when they fix the bug, if you type 0. into the field, it will give badInput===true and therefore valid===false.

like image 192
CupawnTae Avatar answered Oct 21 '22 02:10

CupawnTae


In the spec it says:

User agents must not allow the user to set the value to a non-empty string that is not a valid floating-point number. If the user agent provides a user interface for selecting a number, then the value must be set to the best representation of the number representing the user's selection as a floating-point number.

The spec goes on to say:

The value sanitization algorithm is as follows: If the value of the element is not a valid floating-point number, then set it to the empty string instead.

In Firefox the value in DOM is being set to an empty string for the first input. This is a valid state). If you want the first field to be invalid in this situation, you must make it required:

<input type="number" min="1" max="999" step="1" value="0." id="one" required />

The spec also says:

Constraint validation: While the user interface describes input that the user agent cannot convert to a valid floating-point number, the control is suffering from bad input.

I think in this case what Firefox (32.0) is doing is correct as long as the #one input is not displaying the 0. value. In Firefox if I manually type the value 0. into the #one input then it is correctly highlighted as invalid.

However in Chrome 38.0.2125.58 beta-m the 0. value is displayed when the page has loaded even though document.querySelector("#one").value evaluates to an empty string. This means it fails to be caught by both required as well as by the constraint validation. I believe this is a bug in Chrome.

like image 30
robertc Avatar answered Oct 21 '22 02:10

robertc