Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.0k views
in Technique[技术] by (71.8m points)

javascript - How can I make a text box in React which allows only numbers or an empty value, which triggers the number keypad on mobile?

There are lots of questions like this on StackOverflow, but none of them captures all of my requirements in the same solution. Any help appreciated.

The problem

In my React app, I need a text box with the following characteristics:

  1. It only allows digits to be entered - no minus signs, decimal places, letters, or anything besides just the digits 0-9.
  2. It automatically brings up the number keypad on iOS and Android
  3. I can further restrict the numbers that should be entered, e.g. only allow 4 digits
  4. Leading zeroes are automatically trimmed, e.g. if a user types 02 it should correct to just 2
  5. It allows an empty textbox, and can differentiate between empty and a value of 0

Current code

https://codepen.io/micahrl/pen/RwGeLmo

This code allows typing non-digits, and will just interpret the value as NaN. For instance, the user can type 2f or asdf and the page will say You typed: NaN.

Additionally, while the page loads initially with an empty text box, the user cannot type something and then delete it back to empty. Attempting to delete all text in the input box places a 0 in the box.

Finally, this code doesn't reliably trim leading zeroes, which causes me particular problems because I want to restrict the number to four digits. Typing 01 will not truncate the leading zero; on some browsers, typing 01111 will result in 1111, which is good enough, while on others, typing 01111 will result in 0111, which is a problem.

What I've tried

Because I have set type="number" on the input element, if there is ever a non-number added to the text box, event.target.value in setNumWrapper will be an empty string. This means I can't differentiate between a true empty string (where the user has deleted all text) and invalid input (where the user has typed a non-number, like asdf).

I could set type="text" on the input element, except that I think I need to set it to number to get the number keypad on mobile OSes like iOS and Android.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

With further experimentation, and some help from @Keith in a comment, I've solved almost all my problems.

I've forked the codepen in my question and made these changes in the fork: https://codepen.io/micahrl/pen/GRjwqdO.

Checking input validity

@Keith pointed me to validity.badInput (https://developer.mozilla.org/en-US/docs/Web/API/ValidityState/badInput). With this, I can differentiate between empty input, where a user types something then deletes it, and bad input, where the user attempts to add a non-numeric character.

That means I add this to the beginning of setNumWrapper():

    if (event.target.value === "") {
      if (event.target.validity.badInput) {
        // Set the text box and number to the old value - ignore the bad input
        inputRef.current.value = String(num);
        setNum(num);
      } else {
        // The data in the text box was deleted - set everything to empty
        inputRef.current.value = "";
        setNum(NaN);
      }
      return;
    }

I also have to make an inputRef with useRef(), and set it on the <input> element.

This solves #5 most of #1 (but see below for one remaining minor problem).

Trimming leading zeroes

All I had to do for this was use that inputRef to set the value in the <input> element at the end of setNumWrapper():

inputRef.current.value = String(newNum);

The <input> element's value is always a string anyway, and casting the number to a string removed leading zeroes, if there were any.

Remaining problem: invalid input is allowed if the text box is empty

When the text box is empty, the user can type non-numeric characters into it, and setNumWrapper() doesn't even fire. If you put a console.log() at the top of setNumWrapper(), it won't print anything to the log if the user types a letter, but it will print to the log if the user types a number.

This means I cannot use setNumWrapper() to solve this problem.

However, it's also relatively minor. On mobile, the number keypad comes up, preventing non-numeric input. On the desktop nothing stops the user from typing letters with their keyboard, but for my app, it's clear that only numbers are allowed, so this is good enough for now.

Still, if there's a way to fix this, I'd be curious to hear about it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...