code-for-a-living September 15, 2022

Why the number input is the worst input

Think that web form has got your number? If you used input type="number", you may be surprised to find that it doesn't.

I’ve been writing code for at least 15 years. While I’ve used a lot of technologies and built a lot of stuff over the years, the thing I have done the most is the front end, i.e. HTML, CSS, and JavaScript. And of all the things I’ve built on the front end, the thing that I’ve built most often is forms. 

I won’t pretend to be the expert on all things related to forms. However I will say this: of all the difficult, unconventional, and unusual requirements I’ve been asked to deliver over the past 15 years, most of those tasks revolved around building forms. Each one presented a challenge that required some sort of outside-the-box or non-standard solution. I’ve learned a lot of ways to overcome those challenges: what you should do, and more importantly, what you should not do when trying to meet those challenges.

Of all the things that I’ve learned about what not to do, one of the easiest and best pieces of advice I can share is this: unless the use case is drop-dead simple, you should avoid using the number input.

I’ve seen so many forms that I felt compelled to build my own form builder that could handle at least some of the more challenging requests without the hassle of writing code from scratch every time. Keenforms is a drag-and-drop form builder with a no-code rules engine. It makes it easier to create dynamic and conditional functionality without having to write code from scratch.

Recently I got some feedback about Keenforms, and the criticism I heard the most was the fact that Keenforms does not use the <input type="number" />.* We do have an input that’s labeled as a number, but when you add it to your form, it actually renders as <input type="text" />.

*Please note that Keenforms actually does use the number input inside our app. However, we only use it in the dashboard and for only the simplest inputs.

The pro-number input crowd has legitimate concerns, primarily around accessibility. A number input has native increment/decrement buttons. It has built-in validation to verify it is a number. Certain mobile devices will show a number keypad instead of the full keyboard, making it easier to enter data. Using the number input makes it easier for screen readers as well.

There were also some valid complaints about developers overusing JavaScript (guilty!). I understand some people’s aversion to JavaScript. However those aforementioned forms that I’ve built with those challenging requirements all required heavy use of JavaScript. It’s the only way to provide instant real-time feedback when creating dynamic conditional logic. Keenforms relies heavily on JavaScript and specifically React. 

There are so many drawbacks related to number inputs when dealing with complex and conditional logic, much of it related to JavaScript issues, that for the past few years I’ve decided to avoid using it.

It should be noted I am not alone in the anti-number input camp. The UK Government posted an article detailing some of the problems related to the number input.

The thing is, the issues cited in that article weren’t even the primary reasons why I decided to avoid it. After receiving that feedback on Keenforms, it occurred to me that there are quite a few programmers out there who are completely unaware of the problems the number input presents. The list isn’t long, but the issues are rather jaw-dropping. So here’s a short list of all the horrible things about the number input (at least the ones that I know about) that every developer should know:

  1. When the number input contains an invalid value and you retrieve the value, you get a blank string.
  2. Valid numbers include more than just digits (i.e,. scientific notation like the letter e).
  3. The min/max attributes can easily be bypassed.
  4. Different browsers accept different characters.

When the number input contains an invalid value and you retrieve the value, you get a blank string

There are a couple of ways you might go about retrieving the value. It could be on an event listener, which would store the field value in event.target.value. Or you could get it through the DOM element. Either way when the number is invalid you can’t get the actual value that appears on screen in the <input type="number"> field:

const numberInput = document.getElementById('id_here');
console.log(numberInput.value); // will return empty string if invalid

If you are building a form that requires conditional validations or calculations, it is hard to understate just how big of a problem this is. The fact that the number input will allow a user to enter an invalid number value but not actually retrieve that invalid value (getting a blank string instead), makes the kind of validation I routinely get asked to deliver impossible.

If you are just using a number input for someone’s age or the quantity of a product, this might not seem like that big of a problem. However, consider an example of a form I was tasked with building where the number input either affects or is affected by other inputs, such as a life insurance form with the following requirements:

  • A number input for the quantity of dependents. The number value will determine the quantity of nested forms for each dependent.
  • A number input for each household member that indicates the percentage this person would receive in the event of a payout. If the quantity of dependents is one, then this particular input would be hidden and automatically set to 100. If the quantity is greater than 1, then the input should be visible, and have a value of no less than 0.01, and contain a value less than 100.
  • A hidden number input that would sum total the percentage of each dependent listed, validating that the subtotal is exactly 100.

All of these requirements necessitate using JavaScript. Generally, those might be the minimum requirements, and additional requests usually come in after some user testing. I can’t do my job without being able to access those values, whether they are valid or not.

A highly usable form will leverage JavaScript to prevent users from entering or submitting invalid data. Back-end data validation is not just enough any more. Most of us find it annoying when we go through the trouble of entering data into a form without issue, only to click submit and find out some input was invalid. Using JavaScript for this kind of validation is absolutely essential for the kind of quality user experience most of us have come to expect.

Valid numbers are more than digits

This is the flipside of the invalid number value issue: the number input allows you to enter values that are technically acceptable numbers, but there is a good chance you don’t want them and didn’t anticipate that they could be considered “valid.”

Chances are likely that you used the input type="number” because you expected an integer to represent age or quantity. However, the number input will allow for scientific notation values; for example, 2.3e4, which represents 2.3 times 10 to the power of 4, or 23,000. If the number input value grows large enough, some browsers will automatically convert your input into exponential notation.

Different browsers accept different characters

All browsers are not the same. Let’s start with the best case scenarios.

Chrome and (surprise!) Microsoft Edge

The following characters are permitted (based on my hastily-done first-hand testing);

  • Numbers 0-9
  • Decimal point
  • “-” (for negative values)
  • characters “+” and “e” for exponential notation
Screenshot of Keenforms in Chrome with invalid number value after clicking submit
Screenshot of Keenforms in Chrome with invalid number value after clicking submit

Both of these browsers will prevent you from entering the accepted non-numeric characters more than once. However, you can place those symbols anywhere in the input, like putting the minus symbol in between digits, which would make the number invalid and therefore making that value inaccessible via JavaScript.

Firefox and Safari

There are no limits whatsoever. You can type whatever you want.

All of these browsers will show a built-in popup to indicate that the value you’ve entered is not a valid number, and the Submit button will not work without the user fixing those input values. The built-in validation is visually inconsistent to whatever UX when you are building for your app. As a programmer, you might find this acceptable, but there’s a good chance your designer and/or product manager will not.

Screenshot of Keenforms in Firefox with invalid number value after clicking submit
 Same as above on Firefox

Min/max limits can be bypassed

The ability to set the minimum and maximum number values in the number input is a nice feature to have. The increment/decrement buttons will keep the number value within these range parameters. However, you cannot completely trust it to prevent out of range values; it is possible to copy/paste a value beyond those limits.

<input type="number" min="1" max="10" />

If this seems petty, I want you to know I totally agree with you. But it’s not always my call.

Imagine a software tester finds this issue and logs a bug. Imagine the product manager hears about the bug. Imagine discussing this bug during sprint planning, but then pleading your case to that same product manager: “This is an edge case and the number will still be validated on the back end.” And besides, you said this was MVP and this “bug” is actually standard behavior of the number input. Imagine losing that battle and having to fix it anyway.

The bottom line is that it’s not always the developer’s choice, and the number input is not your friend.

What to use instead of number inputs

Before I go into detail about when to use the number input, it’s important to establish something that many experienced programmers know, but which is worth repeating; You should only use the number input when dealing with mathematically relevant numeric values, i.e. the quantity of a product or someone’s age. That means you never use the number input for things like ZIP codes, phone numbers, social security numbers, etc.

On to the more interesting stuff. Whether to use the number input must be decided on a case-by-case basis, and the calculus will change based on requirements, risks, and stakeholders.

If you need a number input that has no conditional validation (i.e. number is always within a certain range of min/max) and has no relationship with any other inputs on the form, then using the number input is not a big deal. As mentioned, Keenforms does use the number input for the position value of a form attribute. The risks and consequences of a user wanting to do something malicious (like putting invalid number on the position value) to their own form is low enough that it’s a worthwhile tradeoff.

Make sure the stakeholders are aware of this issue of visual inconsistencies across different browsers as well as inconsistencies of whatever messaging you are using to indicate invalid number input values.

If you are dealing with conditional validation or that number is being used elsewhere for a calculation, that’s when you need to ditch the number input. There are a couple of options on how to deal with this issue:

The Gov.UK article mentions a possible solution: Using <input type="text" inputmode="numeric" pattern="[0-9]*"> is a nice option for integers, but it won’t work for floating point decimal numbers.

Your last option is to do what I built for Keenforms: use a simple input type="text“, which will always give you its value, and do all your validation via JavaScript. It requires more effort on the part of the programmer and you lose the increment/decrement buttons. As someone who has learned the hard way, though, it’s a tradeoff that I’ve had to accept. For advanced use cases I have not found another viable option.

That is not to advocate for sacrificing all accessibility for the sake of advanced requirements, visual consistency, and ease of use. I recently spoke with Avram Sand from Access Armada to discuss some of the problems related to the number input.

“The purpose of digital accessibility is to ensure that your forms are understandable and usable for everyone,” said Avram. “Number input fields can help, but there are other ways to get to our desired outcomes, for example, by forcing a numeric keypad on a text input, adding descriptive field labels that indicate the requirements to enter a number, and form validation and errors that work seamlessly with screen readers and other assistive technologies.”

My best recommendation for trying to maintain as much as accessibility as possible is to use an online tool that will check your webpages for accessibility, such as the WAVE accessibility tool.

If there’s one takeaway here, it’s that it’s impossible to deliver the kinds of complex functionality many projects demand while simultaneously using the number input. It’s an either/or choice, and the way I see it, you’re better off ditching the number input.

Tags: , , ,
Podcast logo The Stack Overflow Podcast is a weekly conversation about working in software development, learning to code, and the art and culture of computer programming.

Related