Numeric Inputs – A Comparison of Browser Defaults

Avatar of Geoff Graham
Geoff Graham on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

The spec, purposefully, stops short of telling implementations (browsers) how to handle UI. In this article we’re looking specifically at <input type="number">, and you might be surprised to learn that the spec specifically says:

This specification does not define what user interface user agents are to use.

It goes on to explain that different languages, cultures, and regions might have good reason to handle numeric inputs differently. So browsers are on their own here, and perhaps this time unsurprisingly, there are quite a few different approaches to the UI. Let’s take a look.

The Markup

<form>
  <label for="age">Enter your age</label>
  <input type="number" id="age" name="age">
</form>

This produces a simple form with one single input that accepts numeric values. I added some CSS for presentational purposes in the demos that follow, but this is the basic HTML markup we’re looking at.

How Internet Explorer Handles It

Default behavior in Internet Explorer 11

Internet Explorer offers the simplest default presentation among desktop browsers. The input looks like any other form input that accepts text. In fact, IE is making no user interface decisions for us at all, except the ability to clear the input once something has been entered. That’s a handy little feature, and one that other browsers do not include (although it’s sometimes seen on type="search" inputs).

How Firefox Handles It

Default behavior in Firefox

Firefox introduces UI that IE does not: spinner controls. These controls include up and down arrows that increase and decrease the numeric value of the field when clicked, respectively.

Removing the controls can be accomplished with a little CSS using the appearance property:

/* Remove controls from Firefox */
input[type=number] {
  -moz-appearance: textfield;
}

While that does a nice job of removing the controls, it appears we have no control over the design of them.

How Safari Handles It

Default behavior in Safari

Firefox and Safari are similar in how they treat numeric inputs. Both include spinner controls and both leave out the clearing UI seen in IE.

We can also remove the controls from Safari, but differently than Firefox using a method that tackles the Shadow DOM:

/* Remove controls from Safari and Chrome */
input[type=number]::-webkit-inner-spin-button, 
input[type=number]::-webkit-outer-spin-button { 
  -webkit-appearance: none;
  margin: 0; /* Removes leftover margin */
}

Another element that we can style in the Shadow DOM is the invisible box around the number:

/* Adds a box around the numeric value in Safari and Chrome */
input[type=number]::-webkit-textfield-decoration-container {
  border: 1px #ccc solid;
  background: #efefef;
}

Might be useful, or it might not. Either way, it’s nice to have a little more design flexibility in Safari (and Chrome) compared to what we’ve seen so far.

How Chrome Handles It

Default behavior in Chrome

Chrome sort of falls in the middle of the crowd. At first glance, we’re presented with a simple form input. Then, just as the cursor moves over the field, the same controls in Firefox and Safari are displayed.

Again, it’s interesting and worth noting that Firefox, Safari, Chrome and Opera have decided that quantity controls are enough of a benefit to user experience that they include them where IE has left them out. Then again, Chrome takes the middle ground by revealing the controls on hover as opposed to displaying them by default.

What if we want Firefox to behave like Chrome and display the controls on hover? We can remove the controls from Firefox like we did earlier, then re-apply them on :hover and :focus:

/* Remove controls from Firefox */
input[type=number] {
  -moz-appearance: textfield;
}

/* Re-applies the controls on :hover and :focus */
input[type="number"]:hover,
input[type="number"]:focus {
  -moz-appearance: number-input;
}

While Chrome and Safari might not be as similar as we might expect, they do share an ability to style and manipulate the input using the Shadow DOM elements. In fact, the same techniques covered in the Safari section apply to Chrome as well.

How Opera Handles It

Opera handles numeric inputs exactly like Chrome. That should come as no surprise since Opera adopts the same Blink rendering engine as Chrome. That means that the same CSS tricks to hide and style the input in Safari and Chrome also apply here with Opera.

Since Opera only recently updated to Blink, it’s interesting to see how its last pre-update version, Opera 12, handles numeric inputs.

Default behavior in Opera 12

The difference here is how the the controls float outside the input. Where Firefox, Safari, Chrome and the latest Opera place the controls squarely inside the input, Opera 12 has them completely outside of the field. Even more notable is how Opera has decided to place borders around the controls. This creates what appears to be a full-fledged user interface complete with buttons that extend the width of the input by default.

Removing the controls from Opera is no easy task and can only be removed by changing the input type to text in the HTML and restricting the pattern of accepted characters strictly to numbers:

<form>
  <label for="age">Enter your age</label>
  <input type="text" pattern="[0-9]*" id="age" name="age">
</form>

Another difference is how the number is right-aligned. This is sort of reminiscent of how Excel automatically aligns numeric spreadsheet cells to the right. All other browsers keep the text aligned to the left, so we can force Opera to do the same using CSS:

html:not([dir="rtl"]) input {
  text-align: left;
}

This snippet searches the DOM for any input that is not set right-to-left and forces the text to the left. This would be applied globally as written, but could be modified with more specificity, if needed.

One final detail worth noting is all the highlighting that’s happening on :focus. Opera applies what appears to be the heaviest amount of styling when the field is targeted, all the way down to the quantity controls, which have their own active states when clicked.

How Mobile Safari and Android Handle It

Default behavior in Mobile Safari
Default behavior in Android 4.4

Mobile Safari and the Android Browser are very similar when stacked head-to-head. Both behave much like IE in that they offer no controls directly on the input, but they also diverge from IE in that they provide no added user interface for clearing the input once something has been entered. In this sense, the mobile browsers offer the simplest default implementation of the entire set.

These browsers recognize type="number" and instantly serve a numeric keyboard when the input is in focus. That’s pretty cool and serves as an excellent reminder that adding the proper type to any form field is a good practice for good user experience.

The Results

Here’s a summary of the findings covered in this post.

Does it Have? IE Safari Firefox Chrome Opera iOS Android
Spinner Controls No Yes Yes On Hover On Hover No No
Clearing Control Yes No No No No No No
Left Alignment Yes Yes Yes Yes Yes Yes Yes
Focus Styling On Hover Yes Yes Yes Yes No No
Keyboard Commands Yes Yes Yes Yes Yes No No
Shadow DOM Control No Yes No Yes Yes No No

In Conclusion

It can be frustrating when browsers behave differently. We’ve been dealing with that in various ways for a long time. That’s how hacks—err, tricks— are discovered.

The interesting thing a comparison like this reveals is how these differences have an impact on user experience. If we were to create a numeric form input and rely on browser defaults to handle the display, then it’s very possible that two different visitors to the site will have two totally different experiences. Although that experience might also be consistent for them across sites, since that user likely sticks to one browser.

How do you feel about these UI differences? Do you have a favorite? Have you tried overriding them? What can we do with JavaScript here? Please share in the comments.