简体   繁体   English

React - 如何将电话号码格式化为用户类型

[英]React - How to format phone number as user types

I need to format a 10 digit string to this format: '(123) 456-7890'.我需要将 10 位字符串格式化为这种格式:'(123) 456-7890'。 However, I need this to happen as the user types.但是,我需要在用户键入时发生这种情况。 So if the user has inputted only 3 digits, the input should display: '(123)'.因此,如果用户只输入了 3 位数字,则输入应显示:'(123)'。 If they've inputted 5 digits the input should display: '(123) 45'如果他们输入了 5 位数字,则输入应显示:'(123) 45'

With my current code, the formatting only takes place after the 10th character is entered.使用我当前的代码,格式仅在输入第 10 个字符后发生。 I'd like it so that it formats it from the third character onwards.我希望它从第三个字符开始格式化。

const phoneRegex = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/

const handleInput = (value) => {
  return (
    value.replace(phoneRegex, '($1) $2-$3')
  )
}

class FindASubscriber extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      value: ''
    }
  }

  render() {
    const { label, placeholder, feedback } = this.props
    const { value} = this.state
    return (
      <div className="find__a__subscriber">
        <FlexGrid>
          <FlexGrid.Col>
            <FlexGrid.Row>
              <Input
                feedback={feedback}
                label={label}
                type="text"
                pattern="[0-9]*"
                placeholder={placeholder}
                value={handleInput(value)}
                maxLength="10"
                onChange={
                 (event) => this.setState({value: event.target.value})
                }
              />
            </FlexGrid.Row>
          </FlexGrid.Col>
        </FlexGrid>
      </div>
    )
  }
}```

You can normalize the input like so您可以像这样normalize input

  • the value is up-to-date in relation to event.target.valuevalue相对于event.target.value
  • previousValue is what has already been validated and set to state previousValue是已经被验证并设置为state

This is structured in a way to prevent invalid characters from updating the input and also limits the input to 10 numbers.这是一种结构,以防止无效字符更新输入并将输入限制为 10 个数字。

Click the Run code snippet button below for a working example.单击下面的Run code snippet按钮以获取工作示例。


const normalizeInput = (value, previousValue) => {
  // return nothing if no value
  if (!value) return value; 

  // only allows 0-9 inputs
  const currentValue = value.replace(/[^\d]/g, '');
  const cvLength = currentValue.length; 

  if (!previousValue || value.length > previousValue.length) {

    // returns: "x", "xx", "xxx"
    if (cvLength < 4) return currentValue; 

    // returns: "(xxx)", "(xxx) x", "(xxx) xx", "(xxx) xxx",
    if (cvLength < 7) return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3)}`; 

    // returns: "(xxx) xxx-", (xxx) xxx-x", "(xxx) xxx-xx", "(xxx) xxx-xxx", "(xxx) xxx-xxxx"
    return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3, 6)}-${currentValue.slice(6, 10)}`; 
  }
};

 const normalizeInput = (value, previousValue) => { if (!value) return value; const currentValue = value.replace(/[^\\d]/g, ''); const cvLength = currentValue.length; if (!previousValue || value.length > previousValue.length) { if (cvLength < 4) return currentValue; if (cvLength < 7) return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3)}`; return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3, 6)}-${currentValue.slice(6, 10)}`; } }; const validateInput = value => { let error = "" if (!value) error = "Required!" else if (value.length !== 14) error = "Invalid phone format. ex: (555) 555-5555"; return error; }; class Form extends React.Component { constructor() { super(); this.state = { phone: "", error: "" }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); this.handleReset = this.handleReset.bind(this); } handleChange({ target: { value } }) { this.setState(prevState=> ({ phone: normalizeInput(value, prevState.phone) })); }; handleSubmit(e) { e.preventDefault(); const error = validateInput(this.state.phone); this.setState({ error }, () => { if(!error) { setTimeout(() => { alert(JSON.stringify(this.state, null, 4)); }, 300) } }); } handleReset() { this.setState({ phone: "", error: "" }); }; render() { return( <form className="form" onSubmit={this.handleSubmit}> <div className="input-container"> <p className="label">Phone:</p> <input className="input" type="text" name="phone" placeholder="(xxx) xxx-xxxx" value={this.state.phone} onChange={this.handleChange} /> {this.state.error && <p className="error">{this.state.error}</p>} </div> <div className="btn-container"> <button className="btn danger" type="button" onClick={this.handleReset} > Reset </button> <button className="btn primary" type="submit">Submit</button> </div> </form> ); } } ReactDOM.render( <Form />, document.getElementById('root') );
 html { font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; font-size: 16px; font-weight: 400; line-height: 1.5; -webkit-text-size-adjust: 100%; background: #fff; color: #666; } .btn { color: #fff; border: 1px solid transparent; margin: 0 10px; cursor: pointer; text-align: center; box-sizing: border-box; padding: 0 30px; vertical-align: middle; font-size: .875rem; line-height: 38px; text-align: center; text-decoration: none; text-transform: uppercase; transition: .1s ease-in-out; transition-property: color,background-color,border-color; } .btn:focus { outline: 0; } .btn-container { text-align: center; margin-top: 10px; } .form { width: 550px; margin: 0 auto; } .danger { background-color: #f0506e; color: #fff; border: 1px solid transparent; } .danger:hover { background-color: #ee395b; color: #fff; } .error { margin: 0; margin-top: -20px; padding-left: 26%; color: red; text-align: left; } .input { display: inline-block; height: 40px; font-size: 16px; width: 70%; padding: 0 10px; background: #fff; color: #666; border: 1px solid #e5e5e5; transition: .2s ease-in-out; transition-property: color,background-color,border; } .input-container { width: 100%; height: 60px; margin-bottom: 20px; display: inline-block; } .label { width: 25%; padding-top: 8px; display: inline-block; text-align: center; text-transform: uppercase; font-weight: bold; height: 34px; border-top-left-radius: 4px; border-bottom-left-radius: 4px; background: rgb(238, 238, 238); } .primary { background-color: #1e87f0; } .primary:hover { background-color: #0f7ae5; color: #fff; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id='root'> </div>


Or... have 3 separate inputs and combine them when done.或者...有 3 个单独的输入,完成后将它们组合起来。

 const validateInput = value => { let error = "" if (!value) error = "Required!" else if (value.length !== 14) error = "Invalid phone format. ex: (555) 555-5555"; return error; }; const initialState = { areaCode: "", prefix: "", suffix: "", error: "" }; class Form extends React.Component { constructor() { super(); this.state = initialState; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); this.handleReset = this.handleReset.bind(this); this.setInputRef = this.setInputRef.bind(this); } handleChange({ target: { name, value } }) { let valueChanged = false; this.setState(prevState => { const nextValue = value.replace(/[^\\d]/g, ''); const changedValue = prevState[name]; if (changedValue.length !== nextValue.length) valueChanged = true; return { [name]: nextValue } }, () => { if(valueChanged) this.handleFocus(name) }); }; setInputRef(name, element) { this[name] = element; } handleFocus(name){ const { areaCode, prefix, suffix } = this.state; const areaCodeFilled = areaCode.length === 3; const prefixFilled = prefix.length === 3; if(areaCodeFilled && name === "areaCode") { this.prefix.focus(); this.prefix.selectionEnd = 0; } else if(prefixFilled && name === "prefix") { this.suffix.focus(); this.suffix.selectionEnd = 0; } } handleSubmit(e) { e.preventDefault(); const { areaCode, prefix, suffix } = this.state; const phoneNumber = `(${areaCode}) ${prefix}-${suffix}` const error = validateInput(phoneNumber); this.setState({ error }, () => { if(!error) { setTimeout(() => { alert(phoneNumber); }, 300) } }); } handleReset() { this.setState(initialState); }; render() { return( <form className="form" onSubmit={this.handleSubmit}> <div className="input-container"> <div className="label"> Phone: </div> <div className="parenthesis" style={{ marginLeft: 10, marginRight: 2}}>&#40;</div> <input className="input area-code" type="text" name="areaCode" placeholder="xxx" value={this.state.areaCode} onChange={this.handleChange} maxLength="3" /> <div className="parenthesis" style={{ marginRight: 2}}>&#41;</div> <input ref={node => this.setInputRef("prefix", node)} className="input prefix" type="text" name="prefix" placeholder="xxx" value={this.state.prefix} onChange={this.handleChange} maxLength="3" /> <div className="dash">-</div> <input ref={node => this.setInputRef("suffix", node)} className="input suffix" type="text" name="suffix" placeholder="xxxx" value={this.state.suffix} onChange={this.handleChange} maxLength="4" /> </div> <p className="error">{this.state.error}</p> <div className="btn-container"> <button className="btn danger" type="button" onClick={this.handleReset} > Reset </button> <button className="btn primary" type="submit">Submit</button> </div> </form> ); } } ReactDOM.render( <Form />, document.getElementById('root') );
 html { font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; font-size: 16px; font-weight: 400; line-height: 1.5; -webkit-text-size-adjust: 100%; background: #fff; color: #666; } .btn { color: #fff; border: 1px solid transparent; margin: 0 10px; cursor: pointer; text-align: center; box-sizing: border-box; padding: 0 30px; vertical-align: middle; font-size: .875rem; line-height: 38px; text-align: center; text-decoration: none; text-transform: uppercase; transition: .1s ease-in-out; transition-property: color,background-color,border-color; } .btn:focus { outline: 0; } .btn-container { text-align: center; margin-top: 10px; } .form { width: 550px; margin: 0 auto; } .danger { background-color: #f0506e; color: #fff; border: 1px solid transparent; } .danger:hover { background-color: #ee395b; color: #fff; } .error { margin: 0; height: 24px; margin-top: -20px; padding-left: 26%; color: red; text-align: right; } .input { display: flex; height: 40px; font-size: 16px; width: 33%; padding: 0 3px; background: #fff; color: #666; outline: none; border: 0; } .area-code,.prefix { width: 27px; } .suffix { width: 38px; } .dash,.parenthesis { display: flex; } .input-container { width: 100%; margin-bottom: 20px; display: flex; flex-direction: row; align-items: center; border-top-left-radius: 4px; border-bottom-left-radius: 4px; border: 1px solid #e5e5e5; transition: .2s ease-in-out; transition-property: color,background-color,borde } .label { height: 100%; background: rgb(238, 238, 238); width: 25%; padding-top: 8px; display: flex; text-transform: uppercase; justify-content: space-around; font-weight: bold; height: 34px; } .primary { background-color: #1e87f0; } .primary:hover { background-color: #0f7ae5; color: #fff; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id='root'> </div>

It's all about formatting.这都是关于格式化的。 Any key that prints a character任何打印字符的键
should cause a rewrite of the input field.应该导致输入字段的重写。
This way the user only sees valid formatted field, no matter what he does.这样用户只能看到有效的格式化字段,无论他做什么。

The regex is simple ^\\D*(\\d{0,3})\\D*(\\d{0,3})\\D*(\\d{0,4})正则表达式很简单^\\D*(\\d{0,3})\\D*(\\d{0,3})\\D*(\\d{0,4})

 function getFormattedPhoneNum( input ) { let output = "("; input.replace( /^\\D*(\\d{0,3})\\D*(\\d{0,3})\\D*(\\d{0,4})/, function( match, g1, g2, g3 ) { if ( g1.length ) { output += g1; if ( g1.length == 3 ) { output += ")"; if ( g2.length ) { output += " " + g2; if ( g2.length == 3 ) { output += " - "; if ( g3.length ) { output += g3; } } } } } } ); return output; } console.log( getFormattedPhoneNum("") ); console.log( getFormattedPhoneNum("2") ); console.log( getFormattedPhoneNum("asdf20as3d") ); console.log( getFormattedPhoneNum("203") ); console.log( getFormattedPhoneNum("203-44") ); console.log( getFormattedPhoneNum("444sg52asdf22fd44gs") ); console.log( getFormattedPhoneNum("444sg526sdf22fd44gs") ); console.log( getFormattedPhoneNum("444sg526sdf2244gs") ); console.log( getFormattedPhoneNum(" ra098 848 73653k-atui ") );

You could even get fancier and show underscores where a character你甚至可以更高级,并在字符的位置显示下划线
should be at any given time.应该在任何给定的时间。
Like喜欢
(___) ___ - ____
(20_) ___ - ____
(123) 456 - ____

etc... (let me know if you want this)等等......(如果你想要这个,请告诉我)

Your current code's regex only matches when ten digits are entered (3, 3, then 4).您当前代码的正则表达式仅在输入十位数字(3、3、4)时才匹配。 You could update the regex to accept a range of digits, such as:您可以更新正则表达式以接受一系列数字,例如:

^\(?([0-9]{0,3})\)?[-. ]?([0-9]{0,3})[-. ]?([0-9]{0,4})$

Or you could have the regex simply make sure that 0-10 digits are entered ([0-9]{0,10}) and then split the string yourself into substrings of length 3, 3, and 4. Doing it the latter way seems better since you only want to show certain characters depending on how many digits the user has entered:或者您可以让正则表达式简单地确保输入 0-10 位数字 ([0-9]{0,10}),然后自己将字符串拆分为长度为 3、3 和 4 的子字符串。采用后一种方式似乎更好,因为您只想根据用户输入的数字显示某些字符:

1 -> (1 1 -> (1

123 -> (123) 123 -> (123)

1234567 -> (123) 456-7 1234567 -> (123) 456-7

1234567890 -> (123) 456-7890 1234567890 -> (123) 456-7890

You would have to handle each of these cases which a simple replace won't do.您将不得不处理这些情况中的每一种,而简单的替换则无法做到。

我使用过这个库: https ://www.npmjs.com/package/libphonenumber-js 它有一个 AsYouType 函数,你可以在你的输入中使用它

perhaps, something as the following will work for someone:也许,以下内容对某人有用:

   onChange={(e) => {
                if (e.target.value.length < 13) {
                  var cleaned = ("" + e.target.value).replace(/\D/g, "");

                  let normValue = `${cleaned.substring(0, 3)}${
                    cleaned.length > 3 ? "-" : ""
                  }${cleaned.substring(3, 6)}${
                    cleaned.length > 6 ? "-" : ""
                  }${cleaned.substring(6, 11)}`;

                  handleChange(normValue);
                }
              }}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM