101

I'm trying to exclude minus and plus from input, but it's going wrong:

handleChange(event) {
  const value = event.target.value.replace(/\+|-/ig, '');
  this.setState({financialGoal: value});
}

Render input code:

<input style={{width: '150px'}} type="number" value={this.state.financialGoal} onChange={this.handleChange}/>
4
  • The Regex is correct. Commented Apr 28, 2017 at 19:58
  • 1
    When value for example some number "777" and type "+" get empty input and after can type "+" or "-" more times. Commented Apr 28, 2017 at 19:58
  • Show us the render code for the input. Commented Apr 28, 2017 at 20:13
  • <input style={{width: '150px'}} type="number" value={this.state.financialGoal} onChange={this.handleChange}/> Commented Apr 28, 2017 at 20:15

25 Answers 25

118

Simply way in React

<input
      onKeyPress={(event) => {
        if (!/[0-9]/.test(event.key)) {
          event.preventDefault();
        }
      }}
    />
Sign up to request clarification or add additional context in comments.

8 Comments

This is the best solution because it doesn't move the cursor compared to using onChange + setState. Example - if you filter with onChange event, and the value of 9999, if you try to insert a non numberic between the numbers, etc 99a99, the cursor will skip to the last position. To make things even one step better, you can also add pattern=[0-9]* to double check validity, and use type="tel" to show numberic keyboard on mobile.
So what happen if user paste some string value!!?? It doesn't work
the shortest one: onKeyPress={(e) => !/[0-9]/.test(e.key) && e.preventDefault()}
It doesn't work on Chrome android - keypress is deprecated
Delete, backspace and Enter does not work
|
80

I tried to mimic your code and noticed that there's an issue on React with <input type='number' />. For workaround, check this example and try it yourself: https://codepen.io/zvona/pen/WjpKJX?editors=0010

You need to define it as normal input (type='text') with pattern for numbers only:

    <input type="text" pattern="[0-9]*"
     onInput={this.handleChange.bind(this)} value={this.state.financialGoal} />

And then to compare the validity of input:

const financialGoal = (evt.target.validity.valid) ? 
  evt.target.value : this.state.financialGoal;

The biggest caveat on this approach is when it comes to mobile --> where keyboard isn't in numeric but in normal alphabetic format.

5 Comments

It appears this solution is not valid - developer.mozilla.org/en-US/docs/Web/HTML/Element/input/…
To make it looks better on mobile, you can use <input type="tel" pattern="[0-9*]" ... It does not look exactly the same as a numeric keyboard, but similar, and much better than a regular alphabetical keyboard.
const financialGoal = e.target.validity.valid || e.target.value === '' ? e.target.value : this.state.financialGoal will allow the pattern to be empty, else you will be stuck with a min. of 1 digit
What do you mean by "there is an issue"> Can you please link to a Github issue if there is one?
by specifying type="text" you lose some neat default functionality provided by browser for type="number"
63

one line of code

<input value={this.state.financialGoal} onChange={event => this.setState({financialGoal: event.target.value.replace(/\D/,'')})}/>

4 Comments

This is by far the cleanest answer for only taking positive integers as input. No matter how the user attempts to populate this text input (typing and pasting being the most common ways), only numbers will be allowed.
Yup. This one right here. I tried the accepted answer, but e.target.validity.valid kept returning false, even when matching the pattern.
Only issue is this take cursor to the end when I try to add character in the middle
OP, this one right here!
27

If you want to maintain input type='number' (probably for mobile devices to trigger the numeric keyboard) you should use onInput instead of onChange to capture your event changes.

Using onInput fixed a bug where typing text into a number input would bypass the validation I had assigned to it in onChange. Once I fixed this function to be called in onInput it triggered in all instances.

Here's an example of what I'm doing:

<input
    type='number'
    id={`player${index}Score`}
    className='form-control'
    pattern='[0-9]{0,5}'
    onInput={(event) => this.enterScore(event, index)}
    value={this.props.scoreLabel(this.state.scores[index])}
/>

I hope this helps!

EDIT - 08-03-2018:

I came up with a better solution. Use type='tel' along with a pattern regex within the input component itself.

The nuts and bolts of how I wired this up is here:

class Input extends React.Component {
  state = {message: '3'};

  updateNumber = (e) => {
    const val = e.target.value;
    // If the current value passes the validity test then apply that to state
    if (e.target.validity.valid) this.setState({message: e.target.value});
    // If the current val is just the negation sign, or it's been provided an empty string,
    // then apply that value to state - we still have to validate this input before processing
    // it to some other component or data structure, but it frees up our input the way a user
    // would expect to interact with this component
    else if (val === '' || val === '-') this.setState({message: val});
  }

  render() {
    return (
      <input
        type='tel'
        value={this.state.message}
        onChange={this.updateNumber}
        pattern="^-?[0-9]\d*\.?\d*$"
      />
    );
  }
}

ReactDOM.render(<Input />, document.getElementById('main'));

I have an example of this working on Codepen here

1 Comment

I should also mention, changing the input type from 'number' to 'tel' accomplishes many of the objectives sought by using a number input. You still get a numeric keypad on mobile, and you can still control the input fully, regardless of what input you provide it. In fact I think that's the most logical way to simply solve this problem. There might be more complete ways to do it, but I like the simplicity of 'tel'
24
  • To stop typing, use onKeyPress not onChange .

  • Using event.preventDefault() inside onKeyPress means STOP the pressing event .

  • Since keyPress handler is triggered before onChange, you have to check the pressed key (event.keyCode), NOT the current value of input (event.target.value)

    onKeyPress(event) {
      const keyCode = event.keyCode || event.which;
      const keyValue = String.fromCharCode(keyCode);
      if (/\+|-/.test(keyValue))
        event.preventDefault();
    }
    

Demo below 👇🏻

const {Component} = React; 

class Input extends Component {
  

  onKeyPress(event) {
   const keyCode = event.keyCode || event.which;
   const keyValue = String.fromCharCode(keyCode);
    if (/\+|-/.test(keyValue))
      event.preventDefault();
  }
  render() {
  
   return (
   <input style={{width: '150px'}} type="number" onKeyPress={this.onKeyPress.bind(this)} />

   )
  }
 
}

ReactDOM.render(<Input /> , document.querySelector('#app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<section id="app"></section>

3 Comments

Since it is recommended to use Controlled Components in React, onChange would be enough. onKeyPress is weak in some scenarios like copy/paste.
Does this example not work simply because the type is set to number?
@ThulaniChivandikwa - this example works by using type="number" to reject all keys except 0-9+-. Then it tests for +-, suppressing those.
9

Solution

Today I find use parseInt() is also a good and clean practice. A onChange(e) example is below.

Code

onChange(e){
    this.setState({[e.target.id]: parseInt(e.target.value) ? parseInt(e.target.value) : ''})
}

Explanation

  1. parseInt() would return NaN if the parameter is not a number.
  2. parseInt('12a') would return 12.

3 Comments

'' is not a number, so it does not fulfil the "only numbers" in the question
@SkidKadda - the empty string is one way to indicate "user has not typed any input". Generally, it is a good idea for app code to know this. Usually, when someone wants "only numbers", they really want "only numbers or nothing", so that they know whether the user has typed something, or not.
It's worth noting that 0 is falsy in JS, so the ternary operator in this answer will treat "0" as lack of input (turn it into ""). developer.mozilla.org/en-US/docs/Glossary/Falsy
5

The fastest way I found was adding a onKeyPress like this:

<input 
  type="text" 
  name="example"
  value="Type here"
  onKeyPress={(event) => {
      if (!/[0-9]/.test(event.key)) {
          event.preventDefault();
      }
  }}
>

1 Comment

be aware this property has been deprecated developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event
4

I am using isNaN function of JS to verify whether it is number or not.

const handleChange2 = (e) => {
    if (isNaN(e.target.value)) {
      return;
    }
    const onlyNums = e.target.value.replace(/[^0-9]/g, "");
    setFormData({
      ...formData,
      [e.target.name]: onlyNums,
    });
  };

  const [formData, setFormData] = useState({
    name: "",
    phone: "",
    email: "",
    address: "",
  });

  <input
    value={phone}
    onChange={(e) => handleChange2(e)}
    placeholder="Phone"
    type="text"
    name="phone"
    required
   />

Note:The value stored in the state will string, if you want it to be a number/int just use Number to convert it into integer. ex: let phone= Number(formData.phone)

Comments

3

The most effective and simple solution I found:

<input
    type="number"
    name="phone"
    placeholder="Phone number"
    onKeyDown={e => /[\+\-\.\,]$/.test(e.key) && e.preventDefault()}
/>

1 Comment

The issue with a solution such as this is that it also prevents keys such as Delete or Backspace from working
3

Here is a solution with onBlur, it can be very helpful as it also allows you to format the number the way you need it without requiring any black magic or external library.

2020 React Hooks

const toNumber = (value: string | number) => {
    if (typeof value === 'number') return value
    return parseInt(value.replace(/[^\d]+/g, ''))
}

const formatPrice = (price: string | number) => {
  return new Intl.NumberFormat('es-PY').format(toNumber(price))
}
<input
    defaultValue={formatPrice(price)}
    onBlur={e => {
      const numberValue = toNumber(e.target.value)
      setPrice(numberValue)
      e.target.value = formatPrice(numberValue)
    }}
    type='tel'
    required
/>

How it works:

  • Set initial value via defaultValue
  • Allow user to freely type anything they feel
  • onBlur (once the input looses focus):
    • replace any character that is not a digit with an empty string
    • setState() or dispatch() to manage state
    • set the value of the input field to the numeric value and apply optional formatting

Pay attention: In case your value come from a async source (e.g. fetch): Since defaultValue will only set the value on the first render, you need to make sure to render the component only once the data is there.

Comments

3

Here's my solution of plain Javascript

Attach a keyup event to the input field of your choice - id in this example.
In the event-handler function just test the key of event.key with the given regex.

In this case if it doesn't match we prevent the default action of the element - so a "wrong" key-press within the input box won't be registered thus it will never appear in the input box.

  let idField = document.getElementById('id');

  idField.addEventListener('keypress', function(event) {
    if (! /([0-9])/g.test(event.key)) {
      event.preventDefault();
    }
  });

The benefit of this solution may be its flexible nature and by changing and/or logically chaining regular expression(s) can fit many requirements. E.g. the regex /([a-z0-9-_])/g should match only lowercase English alphanumeric characters with no spaces and only - and _ allowed.

Note: that if you use /[a-z]/gi (note the i at the end) will ignore letter case and will still accept capital letters.

Comments

2
 <input
        className="input-Flied2"
        type="TEXT"
        name="userMobileNo"
        placeholder="Moble No"
        value={phonNumber}
        maxLength="10"
        onChange={handleChangeInput}
        required
      />

  const handleChangeInput = (e) => {
const re = /^[0-9\b]+$/; //rules
if (e.target.value === "" || re.test(e.target.value)) {
  setPhoneNumber(e.target.value);
}

};

Comments

2

Use a pattern validation in order to allow one point (for decimals) and not allow spaces. You need to pass an arrow function in setState() in order to immediately update the state value, once it will be placed back in the input field.

*considering a class component

Given some context to the state variable.

constructor(props) {
  super(props);
  this.state = {inputValue: ''};
};

The input field content is evaluated against the pattern regex. If both match, the event's validity.valid property is assigned as true, otherwise false:

<input 
 pattern = '[0-9]*\.?[0-9]*' 
 type = 'text'
 value = {this.state.inputValue}
 onChange = {this.handleChange} 
...>

In handleChange, the state is immediately updated if validity.valid is true, otherwise it keeps the previous value (and the text in the input field doesn't change).

handleChange = (e) => {
  this.setState((prevState) => (
    e.target.validity.valid ? {inputValue: e.target.value} : prevState)
  )    
};

Comments

2

You can do as follow to allow only numbers, backspace, tab, enter, delete, and arrow keys

<input 
    type="text"
    id="phone_number"
    name="phone_number"
    className="grow"
    onKeyDown={(e) => {
        if (!/[0-9]|Backspace|Tab|Enter|Delete|ArrowLeft|ArrowRight/.test(e.key)) {
            e.preventDefault();
        }
    }}
/>

Comments

1

2019 Answer Late, but hope it helps somebody

This will make sure you won't get null on an empty textfield

  • Textfield value is always 0
  • When backspacing, you will end with 0
  • When value is 0 and you start typing, 0 will be replaced with the actual number
// This will make sure that value never is null when textfield is empty

const minimum = 0;   

export default (props) => {
    const [count, changeCount] = useState(minimum);

    function validate(count) {
        return parseInt(count) | minimum
    }

    function handleChangeCount(count) {
        changeCount(validate(count))
    }

    return (
        <Form>
            <FormGroup>
                <TextInput
                    type="text"
                    value={validate(count)}
                    onChange={handleChangeCount}
                />
            </FormGroup>
            <ActionGroup>
                <Button type="submit">submit form</Button>
            </ActionGroup>
        </Form>
    );
};

2 Comments

Generally, it is a good idea for app code to know whether user has typed some input, or not. In your design, there is no way to distinguish between "user typed 0" and "user didn't type anything". There is nothing wrong with this technique - and I'm sure it is sometimes useful - but IMHO this is a specialized solution.
Yes I agree on the specialized solution. In my mind, this form has a default of 0, so distinguishing is not needed. That case it would adhere to what the OP asked: Only numbers. To me that means that there is no room for null or "" and the storage behind it accepts only numbers as well.
1

We can use the Terinary operator and do like this. Also using useState to set the values to the object

<input id="cost" name="cost" type="text" onChange = {e=>isNaN(e.target.value)?e.target.value="":setItem({...item,cost:parseInt(e.target.value)})} />     

                   

Comments

1

If you want to handle both ., , and (nothing) for the input as a number then you can use this mix:

<input 
  type='number'
  pattern='[0-9]+([\,|\.][0-9]+)?'
  step='1'
  value={this.state.financialGoal} 
  onChange={this.handleChange}
/>

The e.target.value will be a string that can then be parsed to float when needed (like when passed for back-end call). It means that the financialGoal is still a number but should be in a string format.

Decimal value or no number is usually not handled well by default, so this way I have a controlled string input as a number.

class FinancialApp extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      financialGoal: '5,5'
    }
    this.handle
  }

  handleChange = (e) => {
    this.setState({
      financialGoal: e.target.value
    })
    console.log(`${e.target.validity.valid} ${this.state.financialGoal}`)
  }

  render() {
    return ( <
      div >
      <
      p > {
        this.state.financialGoal
      } < /p> <
      input placeholder = 'finanical goal'
      type = 'number'
      pattern = '[0-9]+([\,|\.][0-9]+)?'
      step = '1'
      value = {
        this.state.financialGoal
      }
      onChange = {
        this.handleChange
      }
      /> <
      /div>
    )
  }
}

ReactDOM.render( < FinancialApp / > , document.querySelector("#app"))
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="app"></div>

Comments

0

Define an input with an onChange() method like below (in my case, childState contains the state, passed down to this child component).

<input
   ...
   value={this.props.childState.volume}
   ...
   onChange={(e) => handleChangeInteger(e, {volume: e.target.value})}
/>

One approach I used was to install validatorJS (npm install validator --save)

I then defined a function handleChangeInteger, which takes an object that will be used to change your state, in this case, {volume: e.target.value}. Note: I needed the OR condition to allow my input to be blank, otherwise it would not let the user backspace (delete) the last integer if they wanted the field blank.

const handleChangeInteger = (e, obj_to_return) => {
  if (validator.isInt(e.target.value) || e.target.value == '') {
    this.props.childSetState(obj_to_return)
  }
}

The user will now not be allowed to type anything other than backspace, 0-9, or e (this is a number..) in the input field.

I referenced this post to create my solution: https://stackoverflow.com/a/45676912/6169225

Comments

0

After reading all the answers but none really working for numbers only with copy and paste I came up with this solution.

  • parseInt solution: fails as parseInt("2a") is valid;
  • onKeyup sulution: fails on copy and paste, backspace;
  • key.which solution: will probably fail on copy and paste;
  • type="number" solution: fails on long numbers as javascript converts 40817810000000023511 into 40817810000000023500 because it is 53 bit language
<input
    name="address.line1"
    value={values.address.line1}
    onChange={(event: any) => {
      if (isFinite(event.target.value)) {
      // UPDATE YOUR STATE (i am using formik)
      setFieldValue("address.line1", event.target.value);
    }
  }}
/>

Comments

0

You can try this solution, since onkeypress will be attached directly to the DOM element and will prevent users from entering invalid data to begin with.

So no side-effects on react side.

<input type="text" onKeyPress={onNumberOnlyChange}/>

const onNumberOnlyChange = (event: any) => {
    const keyCode = event.keyCode || event.which;
    const keyValue = String.fromCharCode(keyCode);
    const isValid = new RegExp("[0-9]").test(keyValue);
    if (!isValid) {
       event.preventDefault();
       return;
    }
};

Comments

0
handleChange(event) {
  const value = event.target.value
  let finalValue;
  Number.isNaN(parseInt(value)) ? finalValue = 0 : finalValue = parseInt(value)

this.setState({financialGoal: finalValue});

}

1 Comment

While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please include an explanation for your code, as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion.
0
    <input
                                    
                                    onKeyDown={(event) => {
                            
                                        const filter = ["Backspace", "ArrowRight", "ArrowLeft", "Tab"];
                                        if (!/[0-9]/.test(event.key) && filter.indexOf(event.key) === -1) {
                                            event.preventDefault();
                                        }
                                    }}
                                    
                                />

Comments

0

Instead of using type="number", you can use type="text" like this:

<input
  type="text"
  value={this.state.financialGoal}
  onChange={(event) => {
    const inputValue = event.target.value;
    const sanitizedValue = inputValue.replace(/[^0-9]/g, '');
    this.setState({ financialGoal: sanitizedValue });
  }}
/>

This avoids issues with type="number", such as allowing invalid characters like e, -, or ..

Comments

-1

Maybe, it will be helpful for someone
Recently I used this solution for my App
I am not sure that is a correct solution but it works fine.

this.state = {
    inputValue: "",
    isInputNotValid: false
}

handleInputValue = (evt) => {
    this.validationField(evt, "isInputNotValid", "inputValue");
}

validationField = (evt, isFieldNotValid, fieldValue ) => {
   if (evt.target.value && !isNaN(evt.target.value)) {
        this.setState({ 
            [isFieldNotValid]: false,
            [fieldValue]: evt.target.value,
        });
    } else {
        this.setState({ 
            [isFieldNotValid]: true,
            [fieldValue]: "",
        });
    }
}

<input className={this.state.isInputNotValid ? "error" : null} type="text" onChange="this.handleInputValue" />

The main idea, that state won't update till the condition isn't true and value will be empty.
Don't need to use onKeyPress, Down etc.,
also if you use these methods they aren't working on touch devices

Comments

-4

Set class on your input field:

$(".digitsOnly").on('keypress',function (event) {
    var keynum
    if(window.event) {// IE8 and earlier
       keynum = event.keyCode;
    } else if(event.which) { // IE9/Firefox/Chrome/Opera/Safari
       keynum = event.which;
    } else {
       keynum = 0;
    }

    if(keynum === 8 || keynum === 0 || keynum === 9) {
        return;
    }
    if(keynum < 46 || keynum > 57 || keynum === 47) {
        event.preventDefault();
    } // prevent all special characters except decimal point
}

Restrict paste and drag-drop on your input field:

$(".digitsOnly").on('paste drop',function (event) {
    let temp=''
    if(event.type==='drop') {
        temp =$("#financialGoal").val()+event.originalEvent.dataTransfer.getData('text');
        var regex = new RegExp(/(^100(\.0{1,2})?$)|(^([1-9]([0-9])?|0)(\.[0-9]{1,2})?$)/g); //Allows only digits to be drag and dropped
        if (!regex.test(temp)) {
           event.preventDefault();
           return false;
        }
    } else if(event.type==='paste') {
        temp=$("#financialGoal").val()+event.originalEvent.clipboardData.getData('Text')
        var regex = new RegExp(/(^100(\.0{1,2})?$)|(^([1-9]([0-9])?|0)(\.[0-9]{1,2})?$)/g); //Allows only digits to be pasted
        if (!regex.test(temp)) {
           event.preventDefault();
           return false;
        }
    }
}

Call these events in componentDidMount() to apply the class as soon as the page loads.

1 Comment

Don't use jQuery with React.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.