Hacker News new | past | comments | ask | show | jobs | submit login
The number input is the worst input (stackoverflow.blog)
297 points by HieronymusBosch on Sept 15, 2022 | hide | past | favorite | 269 comments



My personal pet-peeve is devs using the number input for things that contain digits but aren't "numbers" (credit card numbers, 2fa codes, postal codes etc)[0]. Number input provides a lot of footguns and you're usually better off using something else.

[0] https://kilianvalkhof.com/2022/css-html/are-you-sure-thats-a...


A former employer had a system that consumed phone calls from Twilio. The phonecall table had a touchtoneinput column for the sequence of digits a caller pushed after the call connected (press 1 for English, press 3 to dispute a charge, etc.). We would sporadically get these query errors on inserts--turned out the touchtoneinput column was defined as a bigint, and the rare occasions where a caller pressed the # or * keys were sending input that obviously could not be parsed as an integer.

Good rule of thumb: if a reasonable person calls it a number, but your application is never going to perform arithmetic on it[0], it should probably just be a string.

[0]: Yes, a credit card number can be checksummed, but that's more in the sense of a CC# being a sequence of numbers rather than one int.


There are actually 4 more touch tones possible - A, B, C and D.

Sometimes you can get into secret menus if you play the tones for them...


How do I enter these from an iphone? Or should I just let my laptop output the dtmf tones?

I'm def. gonna try this out. I usually pres & and # a couple of times, because it often skips the useless menus and takes me to a person


Historically, you whistled them (https://en.wikipedia.org/wiki/Phreaking), or you bought a hardware ‘whistle’ from the likes of Steve Jobs (https://en.wikipedia.org/wiki/Blue_box)


If you can whistle two tones simultaneously (the D in DTMF means dual), I'd love to hear it.



> should I just let my laptop output the dtmf tones?

Yes. Depending on your OS, it might even have the sound files:

https://osxdaily.com/2016/03/31/play-dtmf-tones-mac/


Cool!, but that doesn't include ABCD.

https://pbxbook.com/other/dtmftone.html does. I'll try a company I know takes at least 40 minutes usually :

Also, something that annoys me about VOIP is that they don't take into account the different tones in different regions. Here in the Netherlands we use 425Hz fo dial tone etc. It always feels very emulated because it's a different tone.


I've found pressing 0 or 1 usually works too


First thing I do when I call an automated line is spam # because that seems to escalate to a human 9x/10.


I've noticed a lot more evil choose-your-own-adventure phone menus these days resort to "Sorry, I couldn't understand" / "Sorry, I still couldn't understand" / "Sorry, your input could not be processed. Goodbye."


Yep, ye tricks of olde like repeatedly saying „help“ or „customer service“ don’t seem to work on those… i recently dealt with an especially frustrating case when moving and buying a brand-new fiber connection. Turns out, they got the activation date wrong, so I called the support hotline mentioned in the confirmation mail. The menu prompted me for my customer ID - which I didn’t yet have - and wouldn’t let me through, even after trying all possible options. In the end, I just called the sales hotline and let them handle the transfer…


I particularly hate the ones that just hang up if you guess wrong three times.


Yep, we had a few IVRs set up like that for clients at an old job. The reality was there for those was that there was no pool of agents we could transfer those calls to, it was set up to be an entirely automated system by request from the client.

It was mainly for doctor appointment reminders, though, so hopefully those called still had their appointments written down somewhere (or also signed up for email reminders).


My thumb rule is always dial the number where the company hopes you'll be handing over money, rarely fails to get me faster to a person who can help or transfer. Schedule appointment, Re-order Supplies, and Billing Questions are always much better than the Cancel service or Tech Support


Do they not just say they'll forward you to the right department and drop you into a hold queue though? I've done this a few times when the support department had a broken automation flow and it got me to the queue, but the people at the well manned department didn't actually do the support for me.


Generally phone systems like that are set up so forwarded calls jump the queue. This is to avoid people getting stuck in a queue for a long time twice.


You're usually better off letting the IVR route you to the first dialogue tree option at least before you start spamming, otherwise you get the 'idiots who can't listen to dialogue tree' receptionist who has to transfer you to someone else anyway but might not get you to the right place on the first try.


I’ve once skipped “Search your problem through our docs” and clicked “Call Support” only to be told off like a pupil that I didn’t read the docs, really like a kid.

It surprised me coming from Apple Business Manager. It was unprofessional. Don’t skip the docs, guys!

Result: As expected, my problem wasn’t solved by following their 18-step DUNS/Apple process during the last 8 month. (My problem: My company changed both address and name, they still have both addresses with the old one as primary).


I'm fine with getting the router human.


I've, um, heard, that more sophisticated phone trees react to profanity by connecting you to a person more quickly, using the logic that you sound pretty upset and need help now.


I ran a network & voice refresh project for a national financial institution a few years ago; during the RFT some vendors pitched a stress analysis element for the contact center & IVR systems. The institution demurred, citing (amongst other concerns) the likelihood of it being gamed.


It's probably smart for a few reasons. Given limited support resources, do you really want to spent all of them on furious people? If it takes five times as long to help one irate customer with a complex situation, at some point that has to enter the calculus.


Can confirm I had grammar for testing for various profanity and took that into consideration for whether to send to customer service in the IVR apps I developed.

The client really didn't want people sent to live agents if at all possible, as they only had a certain low number of agents and wanted to keep it that way, so we had extra prompts that tried to convince the user to give the app a try first before we did a transfer to an agent, but it was something that could start the process off.

And this was an IVR app for a Fortune 500 corporation, not a small company.


What about the sophisticated ones that say, "listen to all or of options as out menu has recently changed." When you call back two years later the message is exactly the same. Why don't they make call handling software that has the menu items assigned to random numbers so that the menu is always different?


I think this and "higher than usual call volumes" are really just about plausible deniability. Anyone who's worked retail will have a story about someone who insists that the thing they're buying was cheaper last week even though it wasn't. Telling people up front that things have changed probably at least sometimes defuses that conversation one way or another before it even happens.


If you have the option of having dual-language queues then you can also opt to the secondary language which generally has fewer callers than the primary. So you get to the front of the line faster, and they also tend to speak the primary language.


Interesting; I'll have to try this. In my experience pressing 0 has a decent chance of working as a shortcut to a human operator (versus choose-your-own-adventure DFS searching for the right node in the menu tree, which can take ages), but obviously it doesn't work when they don't have human operators at all.


Certainly not an Integer, but all that you're saying when you go with String is that the data has no constraints at all.

There's almost always a regular language more constrained and useful than .* that contains the entire set of acceptable terms (although sometimes it'll be a strict superset, in which case DFA acceptance cannot constitute complete validation).


When humans do the input there is often not a single regular language that works, and imposing only one will often frustrate a lot of users.

A really small example of this is postal codes in Canada. For a long time the norm was to put a hyphen between the two clusters: A1B-2C3 but then at some point a lot of websites decided that's bad and they started rejecting it, often with really obtuse messages. This is just annoying and no one is really well served by it. Canada post doesn't care.

And that's not even getting into 0 vs O and the fact that yelling at the user for entering the wrong one is rarely helpful.

I think most of the time the better thing to do is attempt to normalize and ask the user if the normalization is correct. For a numeric thing like a phone number this is probably stripping out non-numbers (other than + at the start) and putting hyphens back in and validating the number of digits you get back, and presenting it to the user if it differs. More work but feels way less hostile to the user.


The regular language should include the fact that you might include a dash between those sets of numbers, e.g. "A1B-2C3".

I really can't think of any case where changing a "0" to an "O" is a good idea (or vice versa), except like base 58 whre you're explicitly avoiding both.

Either way if you're going to normalize you need a regular language, so I think you're kinda missing the point here on what regular languages mean in this context?


That was just a simple example. If you take addresses as a whole, it gets a hell of a lot more complicated. I do agree that you can just `[ -]` in the middle of that one, but simple examples are simple. Also, it's telling that very few sites actually do this.

The real point is that you want to have one regular form for the input in your database, but imposing that one that you prefer on your users creates friction when their experience of the world differs.

As for 0 vs. O, in a Canadian postal code it doesn't matter, because there is indeed a very regular language to them: letters alternated with numbers. A round circly thing in a numeric position is a 0 and a round circly thing in a letter position is an O. No matter what. The only time this actually indicates a problem is if the pattern ceases to hold after the round circly thing, in which case it may or may not be the 0/O confusion that's the problem but it was wrong anyways.

I'm not saying you should store it wrong, but if a user enters "HOH-OH0" and you ask "did you mean 'H0H 0H0'?"[1] and they say yes then you've given the user a much better and helpful flow than "Invalid Postal Code, please fill out the form again."

[1] This is a 'real' postal code btw and it goes to a weird santa-letter black hole even though it doesn't indicate a geographical location that corresponds to the north (an H prefix is for Montreal).


Mayor issue here seems to be not validating and sanitizing inputs before a DB insert.


Or not even being able to log errors and see what the error message says. You don't need to do ahead-of-time sanitization to realize that "Invalid literal for type 'bigint': "120380123#"" means that you should fix your DB type.


>being able to log errors and see what the error message says

A faculty that was, of course, lacking at this particular organization


A string of digits instead of a string of characters to be precise


Years ago, a report somebody had built for us blew up on some addresses in the Midwest. A look at the addresses showed values of the form "12345E State Road 5". The control that read the house number interpreted as a number in scientific format, and of course had no provision for a number that large. Dropping a space in before the "E"s allowed the report to run.

This is an odd case, in that the house number is a "number": in the US it generally indicates distance from some fixed line, and the parity can indicate which side of the street it falls on. But then people will write them with As, Bs, Es, etc. attached.


If something is sometimes a number, then it’s not a number. Address numbers in the US are just identifiers that use digits, and sometimes they are derived from some other factor that is a (or multiple!) number.


Good place to drop the fantastic "falsehoods developers believe about addresses" [0]

And my favourite case of interesting address numbers in the US, menomonee falls, which includes addresses such as "W162N7420 Tamarack Trail" [1]

0: https://www.mjt.me.uk/posts/falsehoods-programmers-believe-a...

1: https://goo.gl/maps/J8s2yMKConwJMg727


In the U.S. it's possible "12345E State Road 5" and "12345 E State Road 5" are different addresses depending on if the streets have directional suffixes and how they're interpreted (east and west)

I'm not sure how mail carrier software works, but in general there's a lot of variations of the same address that still lead to mail getting delivered correctly (and some addresses where the mail can't be delivered at all)

For U.S. apartments, usually any of these work "123A Some St" "123 Some St #A" "123 Some St Apt A" "123 Some St Unit A"


>I'm not sure how mail carrier software works, but in general there's a lot of variations of the same address that still lead to mail getting delivered correctly (and some addresses where the mail can't be delivered at all)

USPS has a canonical list of all valid addresses. In most cases of hand written letters, it comes down to a human’s judgement call.


Canada Post also has a canonical list of all valid addresses.

I live in an apartment, let's say unit 555 at 123 Main St, and my canonical address has the form 555-123 Main St. This is not unusual in any way.

It is vanishingly rare to find a business, software, or customer service agent that is willing to enter this address correctly, even when dealing with my provincial government or my federal government. They always want to put "Apartment" or "Unit", or move the apartment number after the street, or some other transformation.


Also - 12345 State Road 5 E and 12345 E State Road 5 E, and 12345E State Road 5 E


Someone should write a bot to post a reminder to "start with strings unless you're doing math" to reddit/twitter/youtube every week and the world would probably be way better for it. We could even have compiler warnings for type-safe languages when someone declares an int with a name containing 'address/zip/postal/credit/card'


> We could even have compiler warnings for type-safe languages when someone declares an int with a name containing 'address/zip/postal/credit/card'

That sounds like it would be a better job for a linter than for a compiler.


Why not both! Not everyone uses a linter (unfortunately). Compiler would be a nice second catch (for any tooling that has a compiler)


"start with strings unless you're doing math"

lambda calculus tells us, stick with strings even when you're doing math


With lambda calculus you don't need strings


> But then people will write them with As, Bs, Es, etc. attached.

This is fairly commonly used for multi-unit dwellings, especially when someone turns a "normal" house (with a purely numeric street address) into multiple units, including renting out their basement.

In subdivisions the houses are often consecutive numbers (with odd/even numbers on opposite sides of the road), and it's not like the city is going to re-number every other house on the street.


This is also often how you end up with 1/2 addresses - a lot is split and rather than renumber the block, they have one of the lots keep the original and the gets (e.g.) 408 1/2.

And that's why I have a home address that doubles as an input fuzzer.


House numbers are not numbers in plenty of cases. Youy'll probably want to account at least somewhat for international addresses too, since most companies end up dealing with them sooner or later, if only to tell the customer you won't ship there.

I run into this sort of thing somewhat frequently. A multinational food delivery company here had a period for a couple years where it'd parse "1st whateverstreet 123" as an address on whateverstreer with the number "1st", dropping the 123 entirely. This may seem a bit silly but keep in mind that over here "123 Annex B" is a valid house number for instance, which might be a different building from "123B".


I had problems with practically every form that had an address entry when I had an address that was “State Road <number>”. Most validators interpret that as a road called State Road and reject the number. This also caused problems when I was telling my address to people on the phone who were apparently entering it into a form. The road had no alternate name and “Highway 142” didn’t work either.


Quite so. Not far outside Cleveland, Ohio, is the "Parma Triangle" formed by State Road, Pearl Road, and Snow Road. Is State Road a state road? I don't know.


My personal pet-peeve is when devs use any sort of numerical type for things that don’t require numerical operations.

I will never sum up a list of phone numbers, nor will I ever divide my CC number by two.

They’re strings that just happen to be made up of numbers.


https://en.wikipedia.org/wiki/Luhn_algorithm

Credit card numbers are used with numerical operations


That’s a checksum algorithm. You could easily devise something similar for a word based on the characters in it. That doesn’t make it a number.

GP is right. You never do math with two credit card numbers as inputs. You can’t negate a credit card number, or find its absolute value, or square, etc.

It’s just a string of digits.


To reiterate your statement: it's a string with constraints on the glyphs!


Did you even read the wikipedia page I linked?

    If the number already contains the check digit, drop that digit to form the "payload." The check digit is most often the last digit.
    With the payload, start from the rightmost digit. Moving left, double the value of every second digit (including the rightmost digit).
    Sum the digits of the resulting value in each position (using the original value where a digit did not get doubled in the previous step).
    The check digit is calculated by 10 − ( s mod ⁡ 10 ) {\displaystyle 10-(s\operatorname {mod} 10)} {\displaystyle 10-(s\operatorname {mod} 10)}.


I think what GP is saying is: Just because it is a number (that has a checksum built on it being a number), does not mean most programmers should treat it as one; Treat it as an opaque string. If you need to validate it, use a character by character (not digit by digit) Luhn algorithm; Work on ASCII digits, not numbers.

The issue is that many beginner (and some experienced) programmers treat what should be opaque strings as numbers and throw them into a 32/64-bit integer, or worse, a double precision float (in JavaScript). This can result in data corruption if not done carefully. Then, when something comes in to challenge the programmer's expectations, the program breaks. For example, ZIP/postal codes are 5 numeric digits in the US, but in CA, they're of the form "A1A 1A1" (notice the space and letters). JavaScript's `parseInt` will return NaN on a Canadian postal code. Another one is house numbers; They look numeric, but can have letters or even fractions(!) in them.


The first line is revealing; "contains" is not an operation usually performed on numbers.

Does the number 21 "contain" the number 2? Not in a meaningful sense.

However, the string "21" definitely contains the substring "2".


I’m not sure how it’s taught now, or how it might vary by teaching method, but the way I learned multiplication and division definitely treated the number 21 as “containing” the digit 2. I agree with the conclusion that a CC identifier should be treated as a string, but I don’t find this reasoning to be a compelling argument for that conclusion.


It contains the digit 2, but not the number 2. It's a string of digits, not a singular number.


But that’s the difference isn’t it? In a number each digit represents groups of powers of ten. The number of hundreds, thousands, etc in the number.

The digits in a credit card number are meaningless. They have no relation to each other. If you choose two digits next to each other one doesn’t represent groups of 10x as much as the other digit. Or any other quantity.

The digits are effectively just symbols, not quantities like in a mathematical representation.


That excerpt is clearly describing operations on the digits that compose the string and not arithmetic on the CC number as a single integer.


the digits you parse out of a credit card number are used in a numerical algorithm. That's not the same thing at all.


Vehicle identification numbers, which follow an international standard, also have a check digit, even though they contain alphabetic characters.


ISBN codes too - checksum is base 11 so can be represented by a digit 0-9 or an ‘X’ in the final place.


The most infuriating part about number input, is when you set min=0, you can't backspace in the field to enter a different number.


I don't see that behavior on Firefox and Chromium ... What browser dos that ?


Stupid question. If you don't use input[number] on such fields, how do you get a number keypad input on mobile keyboards for them? It's annoying, as a user, to switch to numbers and use the tiny row of numbers to enter credit card info.


Not a stupid question! I believe CSS Tricks has your back.

https://css-tricks.com/finger-friendly-numerical-inputs-with...


type=text

inputmode=numeric


This.

Further, for credit cards, you can add `autocomplete="cc-number"` to the input and it will provide platform-specific UI for pre-filling the credit card number.


Thank you!


My credit card number is three quadrillion, five hundred and twenty-eight trillion, nine hundred and seventeen billion...


just as a bit of trivia, not sure where this even comes from, Dickensian counting houses? but you're not supposed to put the word "and" in there, just strip it out; say "and" when you get to the decimal point


The "and" after "hundred" is just standard British English. American English omits that "and" though.


but it makes an actual difference in unambiguous comprehension, like the Oxford comma.

"one hundred seventeen" vs "one hundred and seventeen" are different, 117 vs 100.17

"twenty three eights" vs "twenty and three eighths" are 23/8 vs 20 3/8.

I don't know why this idea is upsetting to people.

https://www.grammar-monster.com/lessons/numbers_how_to_write...


This is surprising to me because as a British person living in the US, who is familiar with British and American English, I've never once heard anyone use "and" to mean a decimal point. Literally never. The first time I'm hearing about it is from your comment.

I even found this article about how "and" doesn't actually mean decimal point: https://www.grammarphobia.com/blog/2012/02/decimal-point.htm...


It’s a distinction I remember from (American) childhood (I’m ~40) but I think even then it was viewed as pedantic and falling out of common usage. I don’t think either is “right” even if I take a prescriptivist approach, but even if I’m not aware of it I do tend to favor omitting non-decimal “and” in my own usage in hope it’s less ambiguous.


just because you haven't heard of something doesn't mean it doesn't exist.

I think it dates from the time when the only decimals the average person would use were in currency.


And just because it exists, doesn't mean it's "right".

Your link is the only one I could find on the first page of google that states "and" means decimal point. Every other source I could find says "100.17" is either "one hundred point one seven" or "one hundred point seventeen".


most english speaking countries apart from the us (and canada?) say and. Are you saying they are wrong and you are right?


I'm not sure where "one hundred and seventeen" would mean 100.17. To me, as someone from the US, it unambiguously means 117. I probably wouldn't use the "and" myself, but it makes no difference in interpreting the numbers. To say 100.17, I would say "and 17 hundredths", not just "and 17" (17 what?).


imagine you are reading lists of currency amounts off to another person across the room going over ledgers. It's an easy shorthand to adopt to compress more information into the bandwidth. And if you adopt it as a way of speaking every day, you don't need to talk about it any more.

I think people are forgetting, before the days of numeric calculation by machines, there were rooms full of people tasked with keeping track of these things.


I'm not saying "I'm right", I'm saying "this is a system I was taught, the system pre-existed me, and I thought an intellectual website that obsesses over the history of small details and unambiguous communication protocols would appreciate my contributing it."

back when we wrote checks, you didn't write the word "and" till you got to the cents (US) on a line that was preprinted "dollars": one hundred fifty three cents dollars" vs "one hundred fifty and three cents dollars".

It's history, it's interesting, it's not meant to throw you into a panic or fits of anger. And I very much doubt that you speak for all of English speaking countries, and through history as well, but must admit you might and I'm lucky to encounter an historian of numerical orthography.


I can’t speak for everyone, but I’ve never in my 50+-year-life had someone express in American English “and” while referring to a decimal point.

(Update: unless the units were expressed after the following number, like 5 dollars and 13 cents, which still wouldn’t express a decimal point.)


like 5 dollars and 13 cents, which still wouldn’t express a decimal point

it doesn't express a decimal point if you think of cents as a completely different unit of currency from dollars, but if you think of cents as 1/100ths it actually does indicate the decimal point. "5 dollars and 13 100ths" which is what cents means, and which is sometimes used when not referring to money, for example items that are measured in centiles such as interest rates. I think our forebears were more likely to throw words like centile around, and thats how they came up with the word cents.

My cousin is Italian and he always says "for cent" to me and I scratched my head till I realized, per is translated to for from Italian, and they say "25 per cento" so he's just translating it to English as it makes sense to him, not realizing we also have the word per.


Your cheques example is a completely different matter because you’re specifying a unit, without which it would be nonsensical in at least most English locales (all that I know of). That dividing “and” in {{N dollars} and {N cents}} is just a normal “and” like any other in English, roughly meaning “plus”. In Australian English, I would pronounce $1,234,567.89 as “one million, two hundred and thirty-four thousand, five hundred and sixty-seven dollars and eighty-nine cents”.

For more interesting currency matters, go back to https://en.wikipedia.org/wiki/£sd#Writing_conventions_and_pr..., where you had three items, and “£2/4/6” would typically be pronounced “two pound, four and six” (or sometimes “two pounds, four shillings and sixpence”). This shows more clearly that what it’s a perfectly normal “and” like any other in English. (Also that conventions can certainly allow elision in understood contexts. I’ve never heard of “one dollars and twenty-three” for “$1.23”, but it would be consistent with the sorts of shortenings that sometimes happen.)


my tone was probably a bit blunt. It seems to be a stimulus-response in me when regional differences in language, date formats or units of measure are presented as being wrong rather than just different.


the sense in which one way is "wrong" is the same sense in which the oxford comma is argued: nobody is saying that civilization will collapse either way, but one way is tighter in terms of eliminating more common forms of ambiguity. And when saying 150 is one hundred and fifty, the "and" adds nothing in terms of information or comprehension; to that very same person, "one hundred fifty" is just as comprensible.

and fwiw, I don't think it's a regional difference, I think it's a difference between certain traditions within the banking and accountancy areas, vs outside.


the "and" is there in other languages so maybe it's just a translation oversight ;-)


Funnily enough I just dealt with this scenario today. I had to fix a database which was storing phone numbers in an int column. The phone numbers start with a 0 so no surprise querying it didn't work! It's now a string.


Some years ago we had fun with a client who had systems that insist staff reference numbers are zero-padded, but users like processing data via Excel before uploading for us to import or otherwise process. Even just opening and accidentally saving (habit, accidental change followed by undo with enough time between for auto save to kick in, etc) when intending just to open to inspect, will step the leading zeros off in a CSV. So all code dealing with their manual inputs or bulk imports needs to be aware that those codes may or may not have leading zeros so cannot be used as simple string values as they should be. For extra fun, in one case after a company merge/purchase some codes were padded to a different length so treating them as numbers or unpadded strings internally and padding with zeros for display/export/other wasn't as clear as you'd think.


The tendency of spreadsheets, most notably Excel, to convert phone numbers to integers (or worse, a generic number form that it then saves in floating-point representation) has infuriated everyone that ever exported a telephone field.

Yes, there are ways to trick Excel into interpreting the value as a string. None of them are foolproof.


I saw the same thing once for United States zip codes. I guess they didn't realize that the east coast has zip codes that start with 0s


Then you've got your ZIP+4 format ...


This advice sounds awful lot like the old school “don’t use <table>”.

It is a terrible advice, you shouldn’t use <table> for styling, but if you are representing a tabular data, by all means use <table>. I’ve heard some developers (mostly back end devs that dabble in front end) say you’re not supposed to use tables, and I’ve seen div soups where there definitely should be tables. And I blame overuse of the rhetoric “don’t use <table>”.

By all means, if your taking in a numeric value, use <input type=number>. You get frontend validation, localization, context aware soft-keyboard, accessibility etc. all for free. Use it.

If however, you don’t have a numeric value (e.g. a value that is simply represented with digits) use something else. In general, if ordering values makes sense, then most likely <input type=number> is your best choice.


Hmm. The author's problem is not with <input type=number> conceptually, which would be fine if properly supported by browsers, i.e. in the ideal world.

In the real world, however, the browsers apparently a) don't prevent users from entering incorrect values b) don't allow the developer to do anything about it programmatically, by preventing access to these incorrect values (always returning empty).

The author brings attention to this problem.

Now it's up to you as a developer to decide whether the benefits of using <input type=number> outweigh the costs, but the costs are real and you need to be aware of them.


It is supported by all modern browsers. Even IE-11 supports valueAsNumber and the most important Constraint Validation API methods to deal with the problems the author mentions.

If you want to deal with inconsistent (and possible eccentric) user input: Use input.valueAsNumber

If you want to distinguish between empty and invalid inputs: Use input.validity.valid or input.validity.badInput

If you want to programmatically deal with incorrect entries: Again use the constraint validation API.


An easy question to ask: “would it be fine to add or remove an arbitrary number of leading zeroes?” If no, don’t put it in a number field.


i have an insurer who uses social security numbers as account numbers...and stores them as integers.

the developers obviously did not realize that social security numbers may start with a leading zero. (so can postal codes!)


This can clearly cause output formatting issues, but it doesn't seem to introduce any ambiguity as they're all 9 digits.


And when you join up as covered by Medicare (usually around age 65), you get a second 9 character ID on a card that looks like a Social Security card with the hyphens in the same positions, said ID probably containing both digits and letters (not case sensitive AFAIK).


A bit of a side point but I'm a little amazed that using ssns as account ids is legal.

The approach just seems to be asking for identity theft/fraud...


I’m not sure how it could be illegal. It definitely isn’t a good natural primary key (but then I’m firmly in the “natural PKs are harmful” camp anyway). And it’s certainly too easy to leak PII by using it as a PK. But I’m struggling to imagine a coherent legal structure which accepts collecting SSN data but mandates limits on how it can be used in a database schema.


Personally I am conflicted on this one.

On the one hand a ssn number(or any identifying number) should not need to be any more secret than the name it is replacing, your ssn should be able to be publicly available.

However, this is the real world and many people and institutions use the ssn as the sole source of identity, which means it needs to be kept secret.


Unfortunately, about 40 years ago, I was employed by a corporation in a capacity that required them to file documents identifying me by SSN to state regulators in several states, such documents being available for the public to inspect, not withstanding that I still have my original Social Security card, which says thereon quite clearly "Not for Identification."

The SSN does not have the attributes of a good permanent identifier of a unique person. If you get an SSN with three consecutive 6's in it, you can get it changed to another one not containing that string. Worse yet, the Social Security Administration has reserved the right to reuse the SSN's of persons deceased (IDK if they can or might do that without giving further notice). Furthermore, the US government talks as if there are two completely different kinds of numbers that each use 9 digits, and that they might overlap, ie use the same numbers for different entities; one being SSN's and ITINS, and the other EINS. However, the practice of the IRS and SSA has been to do their best to make sure that they do not overlap, at which the results have been as good as can be expected, but not perfect.

As many games have been lost by one card too many as by one card too few.


I actually mentioned that at the bottom of the article on how to handle the issues mentioned. Firmly agreed, using the number input for non mathematical values is a horrible idea.


> 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.

Note that there's also an inputmode="decimal" option which does have a decimal-point on Android and iOS

Really the answer is just, "don't use number most of the time, use regular text with the appropriate inputmode". The inputmode abstraction is better than the type abstraction; it lets you specify what you really care about most of the time (mobile keyboards) without a bunch of extra constraints bundled in there


> The inputmode abstraction is better than the type abstraction

No. inputmode has no localization, or validation.

If your locale uses period as a decimal mark, but mine uses a comma, you can type "42.5" while I type "42,5" and they are both the same numbers (both evaluate to 42.5). You also get localized front end validation for free if you use type=number, but you’ll have to implement it your self with inputmode.


inputmode does localization:

> Fractional numeric input keyboard containing the digits and decimal separator for the user's locale (typically . or ,)

https://developer.mozilla.org/en-US/docs/Web/HTML/Global_att...

As for validation- you can get reasonably far with the `pattern` attribute, but many apps do validation with JavaScript anyway.

The benefit of inputmode is specifically that's it's less opinionated- it lets you give instruction to the system keyboard (which is a feature you can't otherwise provide yourself), but leaves it to you to handle everything else as you see fit.


Only on the soft keyboard, not with the value it self. So for users with hardware keyboards it will matter if they type 42.5 or 42,5 (i.e. the browser sees those as different values under different locales).

And this becomes an even bigger issue if you use pattern for validation (as you will have to sniff for the user’s locale conditionally adjust the pattern; good luck with that).

You also get this and validation error message localized with <input type=number>

EDIT: To clarify further. If I type 42,5 on my browser with an Icelandic locale, but your website uses inputmode=decimal, then my input will evaluate as NaN when you run `inputElement.valueAsNumber`. On the other hand you will get 42.5 if you properly set the type=number.


Sure, so that's a good thing to think about when implementing your logic. But it's not like you don't have to think about localization at all if you use type="number", and imo the downsides outweigh the upside of having the browser handle this one case for you


I feel like you might be underestimating the difficulty of localization. There are way more customary ways to write numbers then you can think of (there is probably a Falsehoods Programmers Believe About Numbers out there which addresses the innumerable ways people write numbers).

I also feel like you are underestimating the amount of localization that the browser actually does. Yes you still have to do some localization, but there is a whole issue that the browser does for you if you use the correct attributes. There are way more upsides which you might be unaware of.


Updating pattern to support float seems viable no?


Not being a front-end dev, it does seem trivial, but using [0-9\.]* means you can have 1.2.3. You can tighten that regex up, but I imagine that either doesn't fly with mobile keyboard hinting or screen readers trying to hint what a correct value is.


This one works: `[0-9]+([\.][0-9]+)?`

> I imagine that either doesn't fly with mobile keyboard hinting or screen readers trying to hint what a correct value is

The `pattern` attribute isn't used for anything except validation as far as I know; the mobile keyboard at least is entirely driven by `inputmode` and/or `type`


It will work until you have someone from Europe get in touch early in the morning to complain your form doesn’t accept decimal commas (although they may well just roll their eyes again and mutter something about how Anglos don’t bother supporting their international users properly).


`[0-9]+([\.,][0-9]+)?` then

Getting into the weeds here though. The advantages/disadvantages of regex-based validation are beside the original point (and anyway I prefer doing validation in JS most of the time, for various reasons, but that's also out of scope)


> I prefer doing validation in JS most of the time

As you should! :) Preferably by using a parser-generator library. Validation is basically just an error mode of parsing, and parsing things with regexes is not only unreadable, but also loses a lot of valuable information about the error.


Luckily it is easy to do validation with javascript if you properly set type=number:

    if (Number.isNaN(input.valueAsNumber)) {
      // If only this message could also by localized
      input.setCustomValidity("Please type a number");
    } else {
      input.setCustomValidity("");
    }
But double luckily, we don’t even need to do this as we also get this for free, and as a bonus the message is localized.


Switzerland is in Europe.

With 1'000'000 percent certainty.


Doesn't work with i18n (some countries use , instead of . as the decimal separator).


Actually the majority of the countries use the comma. Though dot wins by population as China and India use it.

https://en.m.wikipedia.org/wiki/Decimal_separator


Fwiw the pattern can be dynamic. But at that point as others have noted might be better to have a dedicated validator


Until you come to Spain and write

8.320.320,23


Yep you can do that alongside this change


The stepper buttons on the number input are infuriating. They're far too small for anyone to click on, nobody uses them, and they look bad.

Invariably, a designer/PM/someone will come back and say "Is there any way we can get rid of those stepper buttons on the right?" and then it gets changed to a text input with JS validation.


Yes.. but it is connected to Up and Down arrow. When you're trying to build a fast keyboardable UI, I've found it a convenient enough mechanism.

Also.. to this authors point, wouldn't you just use the 'validationMessage' if you're trying to extract the cause of the error from the field. I have yet to find myself in a position where I need the invalid value anyways.


Don’t get me wrong, I love the arrow key functionality and I want to make it accessible, but the lack of ability to suppress the stepper buttons kills it for me


They probably have an entirely different UX on mobile too.


As are the associated change on scrolling behaviour. I keep finding myself adjusting number inputs when trying to scroll the page they're on.


This has got to be some kind of vestige from Windows 3.11, when the spinner control was about 3x larger - relative to the common screen sizes of the time, that is.


I'm baffled as to why in all these years no one just changed them into a pair of diagonally-separated buttons, and increased the width a bit. This would look a little awkward in the world of rectangular everything, but eh. The whole shtick with their size is that they're made to equal the input's height.

Alternatively, of course, custom forms just make the buttons full-size, and I'm pretty sure they could be positioned next to each other: https://i.stack.imgur.com/IHqmt.png


This sounds like victim blaming devs for correctly calling things numbers, but the expected correct answer is something else. There shouldn't be a guessing game as to what the most correct name is that causes everything else to be wrong.

The true fault here is ambiguous naming conventions that become booby traps.

Should we be blaming people for trying to pull open a door that has a pull handle even though there's technically "push" writing on it? It's just so convenient, as the door maintainer, to have just one type of door, though. Am I so out of touch? No, it's the users who are wrong!

Aside from redesigning a lot of things like this that could've been implemented better, we need to do better to avoid ambiguous naming. We should adopt more verbose naming standards. In this case, we could get rid of literal "number" to replace with "number-incremental," "number-money," "number-scientific" etc.

If people try to use just "number" and see it doesn't work, they should be able to quickly find the correct thing without having to decipher a made up nonsense name like "lambda" or scrolling through an alphabetical list of input types and having to spot every possible number type, read its description, and then compare every result to determine the actual correct one.

Yeah, developers can learn with time and it's good to know all the different options... But why aren't we setting people up for success in the first place?


Take it up with the way web standards work.


That appears to be exactly what they were doing.


The worst number input that I've seen is some Windows input, in which the value must always be valid and in the allowed range. So, say, the current value is ‘300’ and you want ‘100’—but you can't backspace over the ‘3’ and enter ‘1’. If the value is ‘350’ and you want ’100’, you can't even select ’35’ and type ‘10’, because the input will freak out midway and immediately replace the value with the allowed minimum. Moreover, how do you even type the number you want if the input can't be empty? If the minimum is 20, you can't ever type anything from scratch without doing some select-replace gymnastics, because every digit will be immediately replaced with the value 20.

Can't remember which app it was, and if it was a standard input from one of the dozen MS frameworks—but the testers on it must've been mightily incompetent. Imagining for a second that someone worked on that is like catching a glimpse into an abyss of pure stupidity. After this horror I'm quite relaxed about problems with inputs that at least allow me to type the damn number.


Reminds me of the inputs that try to be clever with formatting and move the cursor around and insert characters (happens with dates a lot). When you try to backspace, the input cleverly re-inserts the divider and moves you forward again


I accidentally implemented this behavior in a number input recently. Felt stupid after fixing it. This stuff can happen so easily when people don't use the stuff they build.


Yeah this does sound very user unfriendly, although I have to say a good validation UX is particularly difficult to code (When to validate, on submission or on losing focus to that element or instantly on input? Do you need to debounce the validation? How to present validation error messages and where? Etc.).

Your example probably stemmed from the case that the developer wanted the user to have instant feedback on validation, and instead of displaying some validation error message next to the input, they decided f-it - let's just immediately validate on input and not allow any invalid input. Bad/lazy decision, but I can see how it came to be.


Testers may have found it, raised bug, got rejected as non-issue “as spec” whatever. Who knows.


Well, I haven't been a tester, much less in a desktop app or a game company, but I'm vaguely confident that a tester's job is to raise hell, and this sure deserves it. As a ‘senior dev’ I've killed some sponsored, money-backed changes because they would require revising how the site's backend works on a basic level. IMO what this tester should've done is to show the behavior to any dev with a semblance of clout.


It was a nice idea initially but implementation was rather dramatically more complicated than one would think (or at least than I thought it would be) The article mentions some issues but the most horrific one is missing:

If you focus the input field, hold the pointer over it and move the scroll wheel the value is rounded off, increased or decreased. There are 2 gotchas here.

Thus I found myself filling out a form that has to be filled out with the utmost accuracy. I look over the values again and again until the paranoia of potential financial disaster is sufficiently comforted. My eyes circle from field to field 6-10 times. (I'm never again going to make a mistake with this form ever again, enough is enough)

I then scroll to the submit button.

You can imagine what happens next, it involved a lot of adrenaline. One of the fields had changed, it was lower than before and everything behind the decimal point was unchanged. To make matters worse, I had no idea how this happened.

Next time I check all the values 20 times, scroll to the submit button and check the visible values again. So far so good?

I keep doing this and eventually it happens again!

It wasn't until the 5th time I finally deciphered what was going on.

https://jsfiddle.net/d63gv41k/


There should be seperate types:

    <input type=integer>
    <input type=float>
We need 2 different input keyboards on mobile: it should not be possible to type a dot if the input must be an integer.

Currently seems to difficult to get a simple integer input: Chrome and Safari does not interpret step=1 as "no decimals".

Also: the up/down buttons should go. That should be an explicit option as they don't make sense in most cases. Might have a type=count for that.


I was just looking into this today. iOS shows a decimal separator (a comma in the Netherlands) with `inputmode=decimal` and none for `inputmode=numeric`. You can try it out here: https://output.jsbin.com/necuzoj


Also fixed precision (aka currency) values.


Might be woth having a

    <input type=currency currency=DKK>
so the keyboard input can clearly show the currency that is entered. This can be very important in multi-currency applications, e.g. travel (both customer and admin).

Currency attribyte is ISO code: https://en.wikipedia.org/wiki/ISO_4217

Also: <input type=count>

which shows up/down arrows, and is a special case of <input type=integer>


That actually sounds like an idea whose time has definitely come. Anyone here on the HTML standards body (or works for Google in the requisite part of the Chrome team)?

There are other uses for fixed precision besides currency (co-ordinates, tolerances, etc.), but currency would be used a LOT!


Or even have a separate attribute to indicate that it only allows integers and maybe one to enable/disable the up/down buttons.


Very good summary of the issues I've found with it as well. I've had to instruct my developers to never use this input type, and use an Angular directive to limit input values instead.


Although I agree with the point made in this case about the number input (provided "inputmode" is set so a mobile device presents the right kind of keyboard), this kind of thinking drives me nuts:

> As a programmer, you might find this acceptable, but there’s a good chance your designer and/or product manager will not.

I couldn't care less if a designer is unhappy with the native functionality my browser exposes, it's called a user agent for a reason. They probably aren't happy with my custom style sheet either and this idea that designers' whims can override user preferences needs to stop.


First I think in this case the browsers are all stuck with a bad decision made in a different era because they don't want to break back compat, and there's no user or product owner who thinks this is actually the right behavior. Do you expect every user and every product to be happy with bad behavior for philosophical reasons?

Second a browser is less and less a user agent and more and more just an app runtime. It's only the lingering history that separates browsers from native app runtimes and not any real difference in expected semantics about experience control.


> Do you expect every user and every product to be happy with bad behavior for philosophical reasons?

No but I expect them to realise the boundary of their own control.

> Second a browser is less and less a user agent and more and more just an app runtime. It's only the lingering history that separates browsers from native app runtimes and not any real difference in expected semantics about experience control.

This is only true if we let it be true.

I detest browser applications, which almost never are accessible, never use platform-native UI, do not integrate properly with system services. Except in dire circumstances, I simply refuse to use them.

I am in no way willing to give up my uniform platform to appease someone who things that every UI element should be "branded".


I think most of your concern isn't actually relevant to this case. The point of the article is that default numeric input basically just fundamentally doesn't work unrelated to wanting "branding", it just doesn't validate user input in a way that is acceptable to either users or product owners: even if you wanted literally zero visual customization you shouldn't use it, and if you were building a native app you also wouldn't accept that behavior. And while there's a whole area about browsers becoming an app platform, this also isn't strongly relevant here because it's a meaningful problem even when building a trivial form submit.

The choices here aren't branded vs unbranded, it's "does the website form submit validate user input or does it not validate user input?" Everyone wants the form to validate, and you can't reasonably validate input if you use that component, so no one can use the component.


Bizarre, isn't it? The goal seems to be to rely entirely on Javascript and throw away any native OS/browser handling. If your designers/product managers are asking for that it's time to find another job.


Sometimes when I want to depress myself, I try to imagine the total collective engineering-hours ever wasted re-implementing standard OS or browser controls, just because a designer did not like the aesthetic choices of the OS or browser vendor. When I think back to the least intrinsically rewarding programming tasks I've ever had, they were all along the lines of "XyzOS's combo box drop-down is ugly. Here is a Photoshop of how we think a combo box should look. Go write 10K lines of code to re-implement the functionality we could otherwise get with one line."


I totally agree with you. I guess most non-programmers would roll their eyes reading this, but honestly, stop trying to make every single web page a native app replacement. And it's nuts how far we've gone into this. Companies don't want you to run their software natively, which is fair enough. But, somehow THIS is the best anyone comes up with.

It's really, quite a sad state of affairs.


The suggestion about using pattern="[0-9]*" will cause problems if your users copy-n-paste (or just type) a number with thousand separators.

People are probably used to providing input in a way to make the stupid computer happy, but that friction need not exist.


> 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.

I felt sick reading that. We've all been there.


It is actually very common to intercept the submit event and do cleanup on the frontend, it is basically the default behavior for all modern frameworks. And if you do that, you can do nice things like submit a proper JSON (e.g. { "number": 42 } as opposed to { "number": "42" }), or use `inputElement.valueAsNumber` to let the browser handle all the oddities of typing a number in a localized way.


It also responds to the scroll wheel in some browsers, which sounds like a nifty feature except that we started receiving sporadic complaints and bug reports from users who insisted that forms were being submitted with slightly incorrect numeric values.

I spent an embarrassingly long time trying to track down some bizarre number parsing bug or obscure culture-specific behavior before realizing it was actually just users' cursors being over the number input while they tried to scroll, causing them to inadvertently change values just before submitting the form.


Absolutely. The scrolling behaviour is the worst part about number inputs. I don't think I've ever found a case where it is useful.


You want a real challenge? Build a number input that handles localizations, some countries use "," to separate decimals, and allow displaying thousand seperator automatically while typing, but read the value as a normal number during submit.

So something like "12.400,56" turns to 12400.56 and vice versa.

Also, is it just me or does this article seem to be a bit of an ad for "keenforms"? Its mentioned multiple times.


You don't handle localization in the field directly. You let it be "text" and ask your preferred library (which is built by people smarter than you/me) to convert said text to valid number. If it converts then good, now you have a number variable in memory with which you can do whatever. If it fails you pop-up the error message and ask the user to enter a valid number. I find on the fly correction (don't let user type letters, ignore signs, etc) to be confusing to end-user.


Sounds like a problem that should've been solved once and for all already.

To your last point: I think it more or less obviously is an ad for keenforms, yes.


I just ran into this, I thought it would be nice to use a number input for relatively large numbers in a form, but then a user asked for a thousands separator.

I ditched the number input.


Yep, this is known as input masking, and I wish browsers would just natively support this through various input types, because it's been the bane of my existence.

Phone inputs are especially painful to mask since you have to deal with a mind-boggling number of format variations.


The (new-ish) ‘Intl.NumberFormat’ API solves this issue

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


No, it doesn't. You can't apply that to a number input.


It can, you just have to mask the input somehow (which can be very involved). In particular you would use numberFormat.formatToParts() and then add the parts to as the mask element interacting differently based on the type.


I would not provide the user the ability to type a separator, but rather keep their number formatted with them in the appropriate places.


The number input should have been called “scalar stepper”. It’s genuinely useful when you have a scalar parameter and want the user to be able to increment and decrement the value with a given step.


Meanwhile in ecom sites where there's a use case for this (how many do you want to add to your cart?) it's always just a dropdown anyway.


That thing is called a "spinner" almost everywhere. And yep, "stepper" would be good too.


I usually see it referred to a "stepper" or a "spinbox". Yes, a spinbox! http://bucarotechelp.com/design/htmlref/81042102.asp


A lot of confusion around this is because there isn't a general understanding around whether <input type="number"> represents a strict numeric value or if just the input interface should be a numpad instead of a full keyboard


The article lays out a lot of good points, though I think a lot of them can be summarized more as "here are things you may not have realized about the number input, so use it accordingly" rather than "it's the worst."

> The built-in validation is visually inconsistent to whatever UX when you are building for your app.

To me, this is the kind of thinking that takes Web 2.0-isms too far and makes for actually really shitty UXes. (And yes, of course, I know that the responsibility for these peeves lies with product managers, designers and the like _as well as_ developers).

I do not need a picker to pick my state. I know what f---ing state I live in. I do not need to review a list of all states, hoping I'll know it when I see it. In fact, for a semi-structured and rather complicated piece of data like an address, why force the user to partial-parse it anyway? No, the fact that you can type the first letter does not help. Just let me type the abbreviation. I know what it is! I f-----g live here!

I know how to type a g-----n address, especially my own, and it's likely that, unlike you, I actually know how to do it correctly. You know, where my ward goes? Ward? You didn't think of that? My fractional street number or the fact that I don't have one?

I do not need a calendar widget to pick my birthdate, especially when the calendar invariably starts with today and does not offer a straightforward way to type the date in my local format. I know when I was f---ing born. I do not need to look at a calendar and think "Hm, I know it was a Wednesday... maybe some time in the '90s? Better click the tiniest f---ing left-guillemet « I've ever seen 360 f---ing times and then maybe one of those months will ring a bell!"

And also, run your braindead validation when I'm well finished entering my information. You do not need to red-highlight the field just because I put the point somewhere else outside of it. You do not need to give me a popup for every number in my phone number until there are ten digits. You do not need to confuse me by sometimes filling in phone number delimiters as I type, and sometimes prohibiting them from appearing at all. I've had two different phone number fields on the same form behave in both of these ways!

Thinking of form-filling-out as a "user experience" or "live interaction" is okay, I guess, but it can't dominate your thinking to the point that you lose why it's a "form" in the first place. There are lots of use cases that really are analogous to filling out a form, I think a lot of web sites could do a better job of keeping the power of that analogy and using dynamics to enhance it.


The screenshot of this article contains an interest rate input field. The prompt in that field says "enter the interest rate, i.e. 3.25." The use of 'i.e.' (Latin id est, meaning 'that is') is a common error in such prompts. I think they meant 'e.g.' (exampli gratia) meaning 'for example.'

That aside, the presence of the decimal point raises four somewhat problematic questions: (1) how many decimal places are you going to allow on input? (2) how many decimal places will you display back to the user after they enter more decimal places than the typical user is expected to enter? (3) how many decimal places of the input will the application actually use in such cases? (4) How wide do you make the field to display the interest rate, and what do you do if the user enters something that won't fit there?

If you are writing software that should match the practices of more than one financial institution (bank, finance company, insurer, brokerage house, auditor, government regulator ...), the answers to those questions may be all over the map, and the number input widget probably will make it all an even bigger mess.


Let’s throw the baby out with the bath water.

Highly specific types are very useful on screen-limited mobile devices as they allow software developers to implement very specific controls for them.

It ticks me off that now I have to lose my 10-digit input keyboard for these fields and have to use the tiny on-screen keyboard for everything.

Reading the article and the linked UK.gov article, it seems like there are two issues: (1) lack of more specific types leading software developers to (2) make bad decisions related to the number type for things that are numeric but not countable numbers.

How about we just add a few specific types, or a subtype mechanism.

Eg specific types for dates, PINs, and so forth.

We would also need to add either a non-countable number type or a subtype of the existing number type. Under the hood it could even just be implemented as a string limited to digit characters, and would be usable for zip codes, etc.


Frontend problems really are something else. First, a number may include letters and does not correspond to real numbers. Second, JS thought it was a good idea to not care about the datatypes. Then the worst. The mess created by decimals and commas having different meanings in different langs and cultures. A bug I see a couple of times per year and a freaking terrible one cause users enter decimal numbers that are 100 times smaller or bigger than intended.


FYI - I wrote the article, happy to answer any questions


I think you’ve missed a few things.

- not everywhere uses a decimal, some uses the comma and a decimal for thousands. Using a number input, these things are handled for you.

- this seems highly specific for a SPA and not server-side

- “e” is a valid part of a number, I don’t understand why you have issues with it.


'“e” is a valid part of a number, I don’t understand why you have issues with it.'

That's a very programmer-centric take. A very, very programmer-centric take. The use of "e" for exponent in scientific notation is a very computer-oriented take on the problem. Such other places as you may have seen it used are leaks from the computer representation. Nobody else sees numbers that way; scientific notation is generally written as 3.278·10³⁶ (only with better superscripts), which is how it is taught in schools for the most part. There are many contexts where I would say it is not a valid part of a number, because I don't expect my users to know scientific notation, let alone an idiosyncratic rendering of it used only for historical reasons in certain niches of the programming world.


I'm pretty sure it's just 'calculator notation' and not a leak from the computer representation, but rather done because of the limited space available (at least on older calculators).


I'm doubtful of this claim. My guess is that the 1.234E-8 type notation originates in floating-point input and output on computers.

My (c) 1977 version of K&R C explains printf() around page 145, and it supports the %f notation for output of float's as 1.234e-8, etc.

This Fortran IV manual (early-mid 1960s, https://www.math-cs.gordon.edu/courses/cs323/FORTRAN/fortran...) lists the same exponential notation, but with capital E for REAL's and capital D for double's.

This pre-dates the first hand calculator I know of, the early HP's from 1968. These machines used the same notation as the HP-35 pictured below (https://www.hpmuseum.org/rpnvers.htm#num).

The HP-35 calculator came out in 1972. According to the page below, the scientific notation used is of the form

1.234-8

i.e., without the E (see the second row of the gallery).

https://vintagecalc.com/hp-35-red-dot/

Just a couple of data points.


> The use of "e" for exponent in scientific notation is a very computer-oriented take on the problem. Such other places as you may have seen it used are leaks from the computer representation. Nobody else sees numbers that way;

It’s a pain in the arse to write, though, which is why quite a lot of scientists and engineers use e even though they don’t know anything about programming and are very, very far from being CS people.

I don’t know anyone who would enter 310^8 naturally in an input field (besides, using for multiplication itself comes from ancient technological limits, but nobody uses the proper multiplication sign either).

So yes, you need to support e for scientific notations in any input field that can be used for large or small numbers.

> scientific notation is generally written as 3.278·10³⁶ (only with better superscripts), which is how it is taught in schools for the most part.

That’s how we write it in LaTeX and our reports, articles, and such. And presentations if you’re lucky. But again, nobody writes that if presentation does not matter enough to go through the hassle. Certainly not in plain text where you’d have to use Unicode characters, at which Windows is completely incompetent.


Perhaps, but the notation didn't exist before it was invented as part of the Fortran language.


your use of asterisk for multiplication has screwed up the formatting because two asterisks get interpreted as indicating italics


Oops, sorry


I have used a calculator before, and I would expect most users to have used one before too.


> I have used a calculator before, and I would expect most users to have used one before too.

A _scientific_ calculator? Because many cheap calculators will just overflow and not use the scientific notation.

You severely overestimate the... amount of data people retain from school (to put it in nice terms).


Even within the privileged US, you can't expect people to remember obscurities of calculator notation.


I’d hope so, it’s basically taught in the 2nd grade (group 4 here), along with how to read and write and it seems people still remember that ok. Google is also immensely helpful if people forget how to read numbers. /s

I guess the point is, you can parse it and output it in whatever format is appropriate, regardless of how it is stored.


States are also taught in schools and yet many people think New Mexico is outside the US.

If you are creating software for engineers, that's a fair assumption to make (that will understand what an exponent is). The public at large? Absolutely not.


That is a convention only commonplace on graphical calculators and modern scientifics with formatted infix entry. A traditional scientific just shows an exponent in a reserved display location.

A large segment of older adults have never used them.


I’ve been seeing it since I was a child on everything from cheap little eight digit calculators to expensive calculators with rolls of paper to graphing calculators.


> 3.278·10³⁶

As a Brit that looks really weird. I'd expect 3·278×10³⁶.


Brits use middots as decimals?


We call them decimal points. Most software uses full stops (aka periods, because that's what's on the keyboard), but decimal points are normal in typeset and handwritten maths.


yea and more! they use low-dot (period) when multiplying variables, and Roman digit for 1, and does not use the "-rd" ending names for the power of 1000: milliard, billiard…


> Using a number input, these things are handled for you.

That's not my experience. I used a number input field for actual numbers (i.e. amounts of currency), but on some phones (specifically Samsung phones), it would show up with a full stop for decimal points (even though the locale was explicitly set to one with commas for decimal points), and users were unable to enter decimal numbers on Samsung phones.

Unfortunately, the handling of the number input field is extremely browser/OS dependent.

Eventually, I decided to go for text input, with numeric for inputmode, and simply interpret both "." and "," as decimal points (not permitting the user to use thousand separators, but few people would do that for input anyway). A bit awkward, but it worked.

Note: Using text and inputmode=numeric did not technically solve the issue of Samsung phones persistently showing the wrong keypad, but the JavaScript interpretation was the real solution to the problem.


When you say "on Samsung phones" do you mean the native Samsung web browser, or any browser on a Samsung phone? Like does this happen on Firefox or Chrome on the Samsung phone?


I will admit, my testing was limited to the native Samsung web browser and to a capacitor made native app, where the problem occurred. It is very possible that Firefox and Chrome on the phone would behave as expected, but I never tested them, since users were experiencing the problem with the other versions of the app, and I needed to find a solution.

Initially, I thought it was a problem with converting a React app to a native app with capacitor, but I tried the same Android app on a different (non-Samsung) Android phone and it did not have the problem (iOS similarly did not have the problem, both web and native version).


Just read the article. A big part of the article that I can summarize is I don't set the requirements, someone else does. Every project is different. You might be fine with just integers. However I've been tasked with much more complicated requests.

There may be exceptions but I guarantee you in most cases the product manager doesn't want the letter e.

My app was built with Rails, and if you enter the value "3.9e3" which represents the number 3900, Ruby will simply use the number 3 and toss the rest of the value away. Do you really want to add special functionality on the back end to covert that value into the actual value? I surely would not.

Also plenty of people use javascript without it being a Single Page Application. Keenforms is not a SPA, we use React for certain pages, but I wanted to keep server side rendering. Using Javascript is not exclusive to SPAs.


(int) ‘3.9e3’ gets parsed correctly in php, perhaps use a language built for the web and not a general purpose language, or a framework that knows how to parse exponential notation? It sounds like Rails doesn’t understand how software works.

I’ve also been messing with forms for twenty-something years, and I’ve never run into these sorts of problems but I understand the problems you are facing, especially after googling these problems and their solution in regards to Rails. I’ve heard it’s a productive language, but when you have to solve such simple problems, maybe not?

Sorry, I don’t mean to bash on your chosen language which is probably how this is coming across. I’m genuinely intrigued by these stack overflow answers.


"Completely change you application stack and learn a new language so you can parse 3.9e3" is not really an option for most people.


thank you so much for this


Just because you didn't run into these problems doesn't mean they don't exist. The clients I do work for have very complicated requirements and expect a lot of dynamic interactions from the forms they pay for. It could be a non profit trying to determine if a new client's family qualifies for federal benefits based on family size and income level. It could be a gas pipeline company trying to calculate how much natural gas they can deliver on a 60 day contract during summer months versus winter months. It could be calculating the medication dosage per body weight for a pharmaceutical company. There was the life insurance example which I mentioned in the article.

Ruby will parse "3.9e3" properly. However Ruby on Rails param parser tries to prevent bad data from entering the system. So the e gets sliced off. There are things that you can do to prevent data from being entered that way.

However like I mentioned in the article and commented on multiple times its not always my choice. And overwhelmingly the product managers and stake holders I interact with would prefer to not see numbers submitted in scientific notation form. There's no reason why you would want to submit your age like that. It's highly unlikely anyone would need a product quantity value in that format.

And maybe most importantly the people who pay tell me all the time they don't want it. Does that make sense?


3.9e3 is equivalent to 3900 in the same way that 3,900 is equivalent to 3900.

So, Ruby’s number parsing is garbage I guess. Hopefully it at least does this properly for floats?


With ROR it might be transformed via the params parser. If you enter the code below in plain old Ruby environment, like irl or the console, it will properly parse it;

"3.9e3".to_f => 3900.0

Something happens when submitting web based params. I'd be curious to see what other back end languages and frameworks do.


This is what happens:

"3.9e3".to_i => 3

and

Integer("3.9e3")

raises "invalid value for Integer()". Really, nothing to see here. Ruby just doesn't treat "3.9e3" as a valid string for an integer (which arguably is correct IMHO)


Some countries use a comma as the decimal point. So it's not obvious to everyone that 3,900 == 3900


> “e” is a valid part of a number, I don’t understand why you have issues with it.

I think people would have fewer issues with it if the browser transparently converted it, either on entry or when calling .value. It's a somewhat obscure case that's easy to get wrong.

For example if I enter 104e4 into a number input with ID test, I get these results in Chrome/Edge:

> $("test").value

'104e4'

> parseInt($("test").value)

104

> parseFloat($("test").value)

1040000

> $("test").value - 0

1040000

Same with decimal points. It's a weird foot-gun because the solution is only half-there.


If you know you're dealing with a number, use `.valueAsNumber` instead of `.value`. Value is a string per the specification, but `.valueAsNumber` does what you expect (for datetime there is also `.valueAsDate`).


The form is submitted to the server using `value`, not `valueAsNumber`. Or maybe you'd prefer all forms to be submitted using JavaScript instead of just using <form action>?


That is up to the developer. Most devs I know prefer to intercept the submit event, convert the form to a JSON object and send the JSON to the server. However if you don’t want to intercept the submit event, then it is up to your server to parse the eccentric inputs, the browser will send them (almost) as is (almost, as in it will consider the locale, e.g. 13,37 will be sent as 13.37 for some locales).


It depends on what you are using on the back end. So for the record my own app's back end is Ruby on Rails.

If you enter the value "3.9e3" which represents the number 3900, Rails param parser will simply use the number 3 and toss the rest of the value away. Do you really want to add special functionality on the back end to covert that value into the actual value? Most of us would not.

I'd be curious to see how other languages and frameworks handle this issue but the e for many of us is a real problem. Most people aren't even aware of it, and learn the hard way.


That's the Javascript's "everything is a single type, but we use types for polymorphism" footgun, and it's a way more general problem than this. Anyway, every JS developer has been harmed by it and knows about it.


Knowledge is knowing "e" is a valid part of a number. Wisdom is knowing your users probably didn't enter their phone number as a power of 10.


A phone number isn't a number, it's a string of digits. It should not use a number field.

It's not even just digits, it can also start with +.


If I understand correctly. JavaScript itself didn't even think '111,2' is a proper float number. Only decimal point is allowed. So by using it, your input already bugged out. The number input is just wild and it did not support transform the input to a proper number for you.


> to a proper number

A proper number is 123.456,78 in large parts of Europe. If I can't put that into your number control when my browser locale is set to Dutch, your custom number input has failed as much at the broken input we're talking about.

It's not just the specific separators, of course; in India such a number is often written like 1,23,456.78 for example, grouping numbers entirely different. Don't assume everyone is American.


> JavaScript itself didn't even think '111,2' is a proper float number. Only decimal point is allowed. So by using it, your input already bugged out.

I may be misunderstanding the gist of your comment, but surely the only "bugged out" behavior would be to try and call parseFloat on the value of the input directly without accounting for localization. Why should JavaScript's number representation affect whether or not I can enter numbers in the way I do everywhere else? The browser offers an API[1] for displaying numbers in a specific locale after all, though it's annoying that you either have to hack something together using Intl.DateTimeFormat.prototype.formatToParts or use a third party library in order to parse them.

[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


If you use a locale where commas are decimals and use number input, it is a decimal in the code. I have my computer and phone set to en-NL, which has that style of numbers.


- this seems highly specific for a SPA and not server-side

How would you expect users to enter numbers on the server?


form method = ‘POST’ and a submit button. Usually.


A SPA is calling a backend as well... and the backend (SPA or not) still needs to handle the number locale (comma versus decimals), unexpected 'e', etc. Not sure what about this is a JS problem.


FWIW, I’ve never seen an ‘e’ from a number input on the PHP side, probably because it is parsed correctly. I’ve also never seen it from ‘.valueAsNumber’ on the js side either, except rendering big/small numbers but the internal representation was still correct. Using IntlFormat solved that problem IIRC. If you are passing it around as a string, then I imagine it’s a bigger problem.


so... with a number input?


A text input, with form validation / processing done server side, like we've been doing since the 90's.


We've been doing simple JS-based number inputs since the 90s!


Can you give an example of an invalid value in a number input that you can't retrieve, and explain how that's worse than the invalid value in a text field? I'm having trouble figuring out exactly what the problem is there.

As for min/max limits being bypassed and needing to do server-side validation also... well, yes? But, don't you have to do that even if you're using javascript for validation? The user could modify the DOM directly and replace the form control with one of their choosing that doesn't have javascript listeners, or submit a request from the javascript console, or even figure out all the required cookies and everything and submit a request with arbitrary params using `wget`. The server must always validate client input.


Thank you so much for asking so kindly. I'd actually be thrilled to do that.

I built a form that gave a price estimate for a product, and it would change as you modified inputs on the screen. One of the requests was to treat a blank number input as a zero. That particular input was not required. The ideal situation would have been to default the number input to zero, but the product manager wanted to let the user leave it blank.

The problem became evident when the calculations to generate the price estimate treated a blank value and an invalid number value the same. You always get a blank string whether its blank or an invalid number value. To be honest I really hate whoever made this decision with the W3C committee or whoever it was. It makes zero sense that you let a user put it bad data but you won't give the invalid value via javascript. This was a deal breaker. We had to switch to the text input.

It should also be noted that the built in validation for the number input takes precedence over whatever javascript written to generate an error message. There were complaints from the designer about this issue. It may seem like a petty thing but again I don't get to make these decisions, they are made by the client.


So, there's a number needed for a price estimate, but it's optional, and may be zero. I'm still having a bit of trouble here, but let's say it's "number of spare parts". That's a number that could be zero, but if it's more than that you need to charge the customer extra.

Let's say each spare part is an extra $5, so if the user enters "1", that's +$5. and if they enter "4", that's +$20. If they enter "0" that's +$0.

And if the user enters "quux", that comes out as a blank value, so you treat it as "0" and modify the price by +$0.

Which is...fine? What extra price did you want to add to the calculation if the user requested "quux" spare parts? Does anything else even make sense?

If you need to tell the difference between an invalid value and a blank string, you should be able to use "checkValidity()".

Sure, being able to retrieve the literal invalid value might have been nice to have in some ways, but I can't figure out why not being able to get it is a blocker for using the "number" type at all - even with the requested design constraints.


If they enter 'quux' or any invalid number then it should mark it as invalid, but if its blank then treat it as zero. You are correct the validity.valid or checkValidity() could be used in the right way to make the distinction between a blank non required value versus something that is invalid. I literally learned about that less than 24 hours ago.

I'm glad I learned something. And I'm really appreciative the way that you asked, it's been kind of nasty in the comments section.

However this not resolve the visual inconsistency issue for displaying error messages. Many complex forms have their own method of displaying error messages. It requires custom HTML and modifying the DOM. It rarely happens but once in a while the designer wants the invalid value mentioned in the error message. These are all issues that cannot be circumvented with the number input.

I don't love avoiding the number input - but I prefer to keep my job and my client happy. I don't have final say on these kinds of things. Accessibility matters to me, but keeping my job matters more. I've lost a lot of time fighting with the number input.


Doesn't using the gov.uk article work fine for issues 1, 2, and 4?

    <input required type="text" inputmode="numeric" pattern="-?[0-9]+" title="Integers only" />
    <input required type="text" inputmode="numeric" pattern="-?([0-9]+|[0-9]*\.[0-9]+)?" title="Decimal numbers only" />
    <input required type="text" inputmode="numeric" pattern="[0-9]*(\.[0-9]+)?" title="Positive decimal numbers only" />


Thanks for the write-up. I'd be interested to know what the actual reasoning in the complaints were though that Keenforms does not use number inputs. Was it just standards pedantry or were there some actual usability issues that were caused by that descision?

You mentioned inputmode="numeric" a few times as a possible solution. Does Keenforms make use of that?


Is this an advert for Keenforms?


I'll admit I name dropped my own product a lot. As a programmer I'm a little embarrassed, bordering on shameless. But as a single founder it means I have to do marketing, which means I have to be a little shameless.


Off-topic, but why am I confirming my nonchalance about cookies on Stackoverflow/Stackexchange sites like three times every week?

The first bunch of times I could handwave it off as a result of Stackexchange having hundreds upon hundreds of sub-sites, but that doesn't work months into the deal. It's not like I'm on a mission to visit all those sites.


Just a guess but they could be invalidating the cookie controlling whether to display the banner server-side due to a bug.


To bug you in to accepting all the cookies?


I ran into an annoying limitation of number inputs. I want to create an input where users can step up or down by 0.5 at a time. And I want to make it so that if the number starts out as something that does not divide by 0.5, clicking the step buttons will first round up or down to the nearest multiple of 0.5. Well tough luck, that's not possible. If the number input starts with the value 0.01 and a step of 0.5, then 0.0 and 0.5 are not valid inputs, -0.49 and 0.51 are. And if I instead change the step to 0.01 to make sure 0.0 and 0.5 even become valid inputs, then the steps are super small and inconvenient for my use case, basically forcing the user to type the number instead of using the step buttons.


I had a similar issue sometime when I wanted the field to increment in powers of 2 (2, 4, 8, 16, 32, etc). I think the best way to go about it is to create custom spinner buttons which increments and decrements according to your rules, and then use the constraint validation api to set the more restrictive validity if the user inputs a value using the keyboard.


Yeah no. Drives me nuts on the phone when people don't use the numbers field, so the VKB is not in the number pad mode automatically.

A QWERTY vkb in numbers / symbols mode have tiny number keys compared to a number pad.

If browsers validate differently, then fix them.


Field type and inputmode are different things. You can have text field, but with numeric keyboard.

Which you would know if you read the article.


If a non-developer calls it a "<something> number" it's definitely a string for me. Even if you need to occasionally perform arithmetic on these "numbers", you could still store them as strings and parse on-demand.

In my experience, performing a culture-specific decimal.TryParse() at form submission time is the most robust way to validate something like a wire transfer amount. You can also do regex replace javascript stuff, but there are always some dragons in that realm (especially as you cross borders). Your backend is almost always going to be a better place to parse and verify user input.


Some of these problems could be solved by having frontend validation on a number input.

Regardless, I think the award for worst input goes to "datetime-local". The MDN page for it even says "Because of the limited browser support for datetime-local, and the variations in how the inputs work, it may currently still be best to use a framework or library to present these, or to use a custom input of your own". Followed by the date and time inputs, which are a real pain to work with.


I’m surprised at how bad native widgets are in general. They’re inconsistently styled, ugly, hard to override, and not all that usable. It’s nice that they have built in a11y support, but that’s about all they’ve got going for them.

I wish there were a web standard for modular, customizable, styleable inputs and pickers. It would eliminate a lot of needless wheel-reinventing and also make the web more accessible.


Often when I use number-input it's because I want numerical keyboard when people interact with it on a smartphone.

Yes, input type="number" is not the best, but when I ask for 2FA codes or credit card number a metric keyboard makes sense. And this is currently the best way I know to achieve it.


> I won’t pretend to be the expert on all things related to forms

Indeed. All of the issues the author is experiencing can be easily fixed with simple and readily documented javascript methods:

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

> Valid numbers include more than just digits (i.e,. scientific notation like the letter e).

> Different browsers accept different characters

Use inputElement.valueAsNumber and stop worrying.

If you send the form unaltered (that is, without intercepting the submit event), use a custom validator.

https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputEl...

> Min/max limits can be bypassed

Use frontend validation, e.g. inputElement.validity

https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Cons...

There are plenty of weird form elements out there, but input type=number is actually one of the better one. But the author is definitely very right about one thing:

> You should only use the number input when dealing with mathematically relevant numeric values

Mathematically relevant might be to strict. I would say that if ordering the numbers makes sense, then input type=number most likely makes sense as well. If your data is simply represented by a number (e.g. zip codes; 90210 < 98112 is a weird statement) input type=number is the wrong choice.


There have been requests for functionality to use an inputs value for a calculation, like for a price estimate. The requirements were also for a blank input to be treated as a zero. Because of the invalid value returned as blank there is no way to make the distinction between an invalid value and a blank value. The valueAsNumber will return NaN for a blank value or invalid.

The fact that the DOM API returns a blank value for an invalid number is a massive misstep on the part of whoever makes the decisions surrounding the DOM API. The W3C? Whatever, this is a truly awful decision.

I don't know pretend to be the expert. But I've seen a lot of things, and I've learned a lot over the past 15 years. For certain requirements there is no way to clear this problem.


Seems pretty clear cut for me:

    const value = input.value ? input.valueAsNumber : 0;
Or if you want to be super defensive:

    const value = Number.isNaN(input.valueAsNumber) ? 0 : input.valueAsNumber;
The nice thing is that constraint validation will not validate empty inputs unless you put the required attribute on the input.

However if it were me, I would default the input field to 0 (as in <input type=number value=0>) so that most users wouldn’t be confused by an unexpected default behavior.


I can't make this any clearer to you - if you have an invalid value like "ABC" you don't want to treat it like a zero, you want to treat it like an invalid value and not perform the calculation until the user corrects the number.

On the other hand if the input is blank you want to treat the value as a zero and perform the calculation.

The problem of getting a blank string for an invalid value means you cannot make the distinction between the 2. I cannot make it any clearer than this.

Not everyone wants to treat a blank number input as a zero, and the ideal situation would have been to default the number input to zero. However like I said in the article multiple times it's not always my call or decision. That why the native validation and visual inconsistency across browsers as well as non compliance with mockups is a problem I've encountered multiple times. When you you cannot see an invalid value or make the distinction between blank and invalid number you lose control over work flow.

Also you rubbed me the wrong way with the snide "indeed" comment. Talking down to people is not a good way to generate a thoughtful debate. I try to approach these things with a certain level of humility. I don't know everything, I even learned a couple of things from other people when I published this article. Have you considered that maybe you don't know every possible requirement or request that I have?


I see what your problem is (sorry I read too quickly). What you want to do is use the constraint validation API (https://developer.mozilla.org/en-US/docs/Web/API/Constraint_...)

    let value = input.valueAsNumber;

    if (Number.isNaN(value) && input.validity.valid) {
      // or !input.validity.badInput if you want to be specific
      value = 0;
    }
If the field is empty you’ll get 0, if it is an invalid number you’ll get NaN, if it is a valid number, you’ll get that number parsed according to the user’s locale.

The current implementation does allow for variety of use cases, it does allow for developers to distinguish between an empty and an invalid value. (it does not allow developers to distinguish between different valid representation, but I consider that a feature as it accommodates for different user eccentricity and locales; as long as you intercept the submit event and use input.valueAsNumber).

I’m sorry that I was insulting, but I’ve seen a lot of people on this forum with opinions about the state of front end development while not having any sort of expertise, and only rudimentary experience (as their expertise lies elsewhere). In your article you complain about front end feature that we front end developers use all the time (even you admit to using it), you complain that it is lacking, but you don’t even mention the techniques we use around those issues. There is no mention of input.valueAsNumber in your article, neither do you mention constraint validation API. Both are tools which allow us to deal with the issues you mention.

Elsewhere in the thread there are people recommending that you simply don’t use <input type=number>, this is a horrible advice (you your self admit there are use cases for it). But this is what happens when you misrepresent the techniques that front end developers use to deal with issues in the platform (or, worse, don’t mention them at all).


The edge case I cited and your solution works. This method will help make the distinction between a non required number input containing an invalid value versus blank. I learned something today. Thank you for this.

I'm going to read more about the constraint validation API.

I stand by my criticism of the fact that the DOM API does not give you the value that appears in the number input. It's actually infuriating.

Also your solution does not solve the problem of a stake holder or product manager or designer who is unhappy about the visual inconsistencies across different browsers and the different validations. Yes the built in validation is useful. However for many of the clients I work for they have high expectations and different demands. The solution you're providing might meet the technical requirement of treating blank like a zero but it would not be accepted by the clients I deal with.

The built in browser validation takes precedence over the javascript validation that is custom built. This has become a massive problem. Even with the ability to customize the error message that doesn't mean the people who are designing or paying for whatever I'm building will find it acceptable. I haven't tested it but I'd be curious what would happen with a large form and multiple native errors, and how do you direct the user to go to each of the inputs that have errors.

I'm not going to list every possible use case, but I've seen a lot. Once you cross a threshold of complexity with javascript form validation the native validation becomes a hinderance that is hard to overcome.

I know there are people on this forum with opinions without having the level of expertise that merits making comments. Not reading someone else's comment while being insulting doesn't help either.

And as far as I'm concerned avoiding using the number input is the least worst option for many of the projects I'm involved with. I could expound upon the input.valueAsNumber method in an update of the article, that's useful in a certain but not all cases. I stand by the content of the article and based on the clients I serve I plan to continue to avoid using the number input. The invalid value as blank string is a deal breaker.


> The built in browser validation takes precedence over the javascript validation that is custom built.

What I do is I actually implement my custom validation with the constraint validation API (input.setCustomValidity()) and display the errors from there (using input.validationMessage). The custom validation actually takes president over other errors, if you don’t want that you can conditionally only set custom validity to a non-empty string when input.validity.valid === true.

There is one annoying part about this method, it is that invalid form will prevent the submit event from firing. This means that you have to put novalidate attribute on your form and fire the validation manually (using form.checkValidity() during the submit event; don’t use form.reportValidity() unless you want the browser to impose on your UI design). Now you can do whatever you want during the submit event, including: a) preventing it if the form is invalid, b) displaying error messages however you want by manipulating the DOM, and c) move the focus to whichever invalid input element you want.

I know this is involved, but it isn’t hard for a front end developer that knows what they are doing. The point is that a front end developer knows how to work with the platform (not against it) by using the same APIs as the platform does. In the case of <input type=number> that API is the constraint validation API. If you don’t use it, but still want custom behavior, you are going to have a bad time, and that is not the platform’s fault (and certainly not the fault of any one element in it).


Not returning the invalid value is in fact the fault of the platform. On more than one occasion I've been asked to put the invalid value in the error message.

If you are using a framework like React or Angular, which I do a lot, then you are using a different approach to validation. Typically you aren't using the DOM API, each framework has it's own approach. You can use the DOM but that is additional work and deviates from the tools provided by each of these frameworks. I probably should have stressed that much of my work is spent using these frameworks. The angular form API is hot garbage but again it's not my choice, I have to use what's been selected. I'm a big fan of React, I have not explored all the options available with React and accessing the value. Typically you aren't using checkValidity when you use React.


I use these frameworks as well, and generally when I’m scouting for libraries I make sure they work well with the platform. A good form validation for react, angular or vue will most likely use the constraint validation API under the hood, if they aren’t, then they probably provide most of the same features (or maybe it isn’t such a good library).

Rolling your own isn’t too hard either. In my job I rolled my own useValidation() for vue in less then 150 lines of TypeScript

If you need the actual invalid value for some reason (which is really rare IMO) you might have a point, but otherwise <input type=number> is a perfectly cromulent element.


And while we are on the subject it should be noted that number params submitted with exponential notation, i.e. 2.47e3 will not always be converted to a number. The app I built uses Ruby on Rails. While Ruby is smart enough to convert the scientific notation, the params are sanitized, so that everything up to and including the e is dropped, so 2.47e3 which represents 2,470 will be saved as 2. Every back end will handle things differently.

On top of that the visual inconsistencies across browsers is a big issue. The native functionality of the number input will not be approved by most product managers and designers.


Most people I know actually intercept the submit event and convert the form to a JSON object before sending to the server. This is in fact the default behavior for most front end framework. If you do that then you can use input.valueAsNumber and it won’t matter how the user types the number as long as the browser understands it. Your product manager should be happy that their European users can type 13,37 while American users can type 13.37 and everyone is happy.

The visual inconsistencies have been figured out a long time ago. I don’t think it would be easy for you to find a competent front end developer that is unable to style a number input according to—or close to—your designer’s spec. I’ll admit that styling validation errors is a bit involved, but if you are using the constraint validation API (as you should) you are doing that anyway regardless of the input type.



HTML inputs have APIs for checking their validation state. The input returning "" in invalid state is easy to test for:

const num = numInput.checkValidity() ? numInput.value : 0; // or some other default


a fun one I've found is that the Samsung keyboard on Android won't let you input negative numbers... they just omitted (-) entirely from the layout.


And the search input doesn't allow disabling auto-uppercase.


really nice ad for Keenforms!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: