Restrict HTML input field to decimal I'd like to restrict user input in a form to a decimal with max two numbers before and 4 after decimal point.

Valid values would be:


50
0.1
1.23
4.5678
12.943



I'm currently doing the following:



$(function() {
  $('input').on('input', function() {
    this.value = this.value
      .replace(/[^\d.]/g, '')             // numbers and decimals only
      .replace(/(^[\d]{2})[\d]/g, '$1')   // not more than 2 digits at the beginning
      .replace(/(\..*)\./g, '$1')         // decimal can't exist more than once
      .replace(/(\.[\d]{4})./g, '$1');    // not more than 4 digits after decimal
  });
});
&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;
&lt;input type="text" /&gt;




This works just fine and I'm happy with the JS replace() method of restricting form input (yes I am doing server side validation as well).

I'm mostly interested to know if there is a more concise way to set up the regex(es) so I can reduce and/or eliminate some of the code here.  It seems like I should be able to get the same result with fewer replacements.
I think that you are going about this the wrong way. Rather than thinking of what you should remove, you should think of what you should match.

You are only allowing zero to two digits before a decimal place and so you can use the pattern \d{0,2}.

To include a decimal place you can use the pattern \.?.

And finally you can include zero to four numbers after the decimal place with the pattern \d{0,4}.

If you were to merge these together you will end up with \d{0,2}\.?\d{0,4}.
This has the problem of matching both fullstops, and numbers 6 digits long.

To amend this you can can use the or operator (i.e. |) but you will need to duplicate your regex.
And can result in \d{0,2}\.\d{0,4}|\d{0,2}.?|.?\d{0,4}.

As an alternate to this you can thank that you only include the second half if there is a decimal, and limits the input to two numbers rather than four. Via \d{0,2}(\.\d{0,4})?.

Both the above patterns have their pros and cons. Now you want to match these and so you don't want to use replace. This gives you the option of exec or match. I'd use match due to the simplicity. As you always want the first match and this will always return a match you can simply use something like str.match(//)[0].

This can result in:



$(function() {
  $('input').on('input', function() {
    this.value = this.value.match(/\d{0,2}(\.\d{0,4})?/)[0];
  });
});
&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;
&lt;input type="text" /&gt;




This has the problem that, if you enter 12.1234 and then change it to 123.1234 it'll be replaced to 12.
This is as you want it to ignore all numbers after the first two and match the decimal and the last four.
To explain this with the demonstration of capture groups you want (12)3(.1234) and then you'd just join them together.
To achieve this you will want to add capture groups to the above, and use a non-capture group to get empty strings, so (\d{0,2})((?:\.\d{0,4})?).
This doesn't capture the 3, and so to add this we'll need to capture everything apart from a decimal point. [^.]*.
Put this in the above function and you should get what you want. (\d{0,2})[^.]*((?:\.\d{0,4})?).
To properly use this we'll need to use exec rather than match too.

The other problem with the above is as 200_success says:


  For example, if you type 12 into the text field, then place the cursor before the 1, and press x, your code is able to discard just the x.
  Joe's answer, on the other hand, discards the x and everything after it.


You can change the single regex to account for this input x12.
But it won't work with 1x2 either.
To account for this we can use your first replace to limit the input to just numbers and decimals.

And, without adding 200_success' position stuff, should result in:



$(function() {
  $('input').on('input', function() {
    match = (/(\d{0,2})[^.]*((?:\.\d{0,4})?)/g).exec(this.value.replace(/[^\d.]/g, ''));
    this.value = match[1] + match[2];
  });
});
&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;
&lt;input type="text" /&gt;




This as far as I can tell works exactly as yours does.
But there is one difference, that I don't think you really want.
If you copy and paste 1234.12345678 into both my input and yours they're different.

Mine results in 12.1234, where yours results in 124.1234678.