patternjavascriptMinor
Restrict HTML input field to decimal
Viewed 0 times
fielddecimalinputrestricthtml
Problem
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:
I'm currently doing the following:
This works just fine and I'm happy with the JS
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.
Valid values would be:
50
0.1
1.23
4.5678
12.943I'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
});
});
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.
Solution
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
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
If you were to merge these together you will end up with
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.
And can result in
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
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
This can result in:
This has the problem that, if you enter
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
To achieve this you will want to add capture groups to the above, and use a non-capture group to get empty strings, so
This doesn't capture the
Put this in the above function and you should get what you want.
To properly use this we'll need to use
The other problem with the above is as 200_success says:
For example, if you type
Joe's answer, on the other hand, discards the
You can change the single regex to account for this input
But it won't work with
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:
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
Mine results in
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];
});
});
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];
});
});
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.Context
StackExchange Code Review Q#128036, answer score: 7
Revisions (0)
No revisions yet.