6

I am trying this below code and the regex isn't working and it is allowing all the characters in the input box.

Desired: Input text box should not accept any other character other than numbers and decimal point.

<html>
<body>
    <input type="text" onkeypress="myfunction(event);"></input>
<script>
    function myfunction(e){
         var p = new RegExp(/^[0-9]+([.][0-9]+)?$/);
         return e.charCode === 0 ||   p.test(String.fromCharCode(e.charCode));      
    }
</script>
</body>
</html>
10

7 Answers 7

11

Here's an alternative way. I'm using the oninput event that is triggered on every value change by user (not only key presses). I'm saving the last valid value and restoring it whenever the new value is invalid.

<input type="text" id="test1" oninput="validateNumber(this);" />
<script>
var validNumber = new RegExp(/^\d*\.?\d*$/);
var lastValid = document.getElementById("test1").value;
function validateNumber(elem) {
  if (validNumber.test(elem.value)) {
    lastValid = elem.value;
  } else {
    elem.value = lastValid;
  }
}
</script>

In contrast to most other answers here this works flawlessly with all input techniques like drag'n'drop, copy'n'paste etc. It also supports special control keys like Ctrl+a (for selecting contents), Pos1, End and so on.

Sign up to request clarification or add additional context in comments.

7 Comments

There is a minor drawback related to user experience with this approach. Whenever a user puts a cursor somewhere inside the already typed value and types something that does not match the pattern, the cursor jumps at the end of the input.
Good point, @WiktorStribiżew. I think this is because we're replacing the whole string on wrong input. I'll think abot a fix for that.
You need to store the place of the cursor, and then put it back. I once tried that way, and it involved much code :(
I just played around with the oninput event but it doesn't offer the information about cursor position before the change was done. You'd need to save the cursor position on every mousedown and keydown event, which indeed sounds like terribly much code for little effect.
@Nick Try changing the RegEx to /^\d*\.?\d{0,2}$/. The quantifier tells it to match between zero and two digits
|
2

You are passing 1 char each time to the myfunction function, so you cannot check the whole value with /^[0-9]+([.][0-9]+)?$/ regex to make sure your format is adhered to.

Check if you input digits, or only allow typing a dot if there is no dot in the input field yet:

<input type="text" id="test" onkeypress="return myfunction(event);" />
<script>
function myfunction(e) {
  return e.charCode === 0 || ((e.charCode >= 48 && e.charCode <= 57) || (e.charCode == 46 && document.getElementById("test").value.indexOf('.') < 0));
}
</script>

2 Comments

This way it works. I tried this before. I was hunting for a solution with regex alone giving me a "one liner" validation. Anyways, it seems I cant use to regex for this.
This fails on drag'n'drop and copy'n'paste. It also breaks keyboard-based copy'n'paste.
2

Try to store the string in a "buffer":

<html>
<body>
    <input type="text" onkeypress="myfunction(event);"></input>
<script>
    var inputString = "";
    function myfunction(e){
         if (e.charCode === 0)
           return true;
         var p = new RegExp(/^[0-9]+([.][0-9]+)?$/);
         var testString = inputString + String.fromCharCode(e.charCode);
         if (p.test(testString))
           return true;
         inputString = testString;      
    }
</script>
</body>
</html>

Take with a bit of salt since I did not test it :)

You could also just read the contents of the input, but that is not necessarily pretty. On the other hand, with my solution you'll have trouble with stuff like backspaces or users clicking in the middle of the text and typing this way. This is just to make you aware of the limitations.

Comments

0

Should add a "return" prior to the function.

HTML:

<input type="text" value="" onkeypress="return myfunction(event)" />

JS:

function myfunction(e){
  var p = new RegExp(/^[0-9]+([.][0-9]+)?$/);
  return e.charCode === 0 || p.test(String.fromCharCode(e.charCode));
}

Regarding the Regex:

the problem with the regex is the the "." is a part of a subset that has a number after it. So: "5." is invalid but "5.3" is valid. However, the function runs for every key press!

So let's try to write: 5.3 which should be valid:

5 - valid

. - invalid

3 - valid

The solution would be to allow a "." on its own as long as it comes after a number.

After that another validation would have to occur in order to make sure that there are digits after the ".".

JSFIDDLE

3 Comments

Return made it functional but it doesnt accept decimal now.
This answer tells why the OP's solution didn't work but it doesn't offer a solution.
The first part solved one problem, the second part is a guideline on how to solve the regex issue ("dot"). @Wiktor's answers focus on the solution of that part.
0

I modified @Wiktor Stribiżew Solution to allow insert from the clipboard and also make sure you and I can use this solution on multiple input elements without targeting one single element.

<input type="tel" onkeypress="return allowOnlyNumbers(event);" oninput="return allowOnlyNumbers(event);"/>

Things to note

  1. The [oninput] would help us with insert from the clipboard.

  2. The [onkeypress] would help us with single characters.

    function allowOnlyNumbers(e)
    {
        // on input
        if (e.inputType == "insertFromPaste")
        {
            var validNumber = new RegExp(/^\d*\.?\d*$/);
            return (!validNumber.test(e.target.value) ? e.target.value = '' : e.target.value);
        }
    
        // on key press
        return e.charCode === 0 || ((e.charCode >= 48 && e.charCode <= 57) || (e.charCode == 46 && e.target.value.indexOf('.') < 0));
    }
    

Comments

0

If you are using jquery you can do the following

let lastValid = $("#Control").val();
$('#Control').on('input', function () {
    var validNumber = new RegExp(/^\d*\.?\d*$/);
    var newValue = $(this).val();
    if (validNumber.test(newValue)) {
        lastValid = newValue;
    } else { $(this).val(lastValid); }
});

Comments

0

Here is my solution for StimulusJS and Rails. I have 2 different regexes for that. First of alle I check if the input is a decimal. I do it with this function which has support for German decimals:

export function decimalMatchFor( lang, value ) { 
  if (lang == null || lang == "") {
    return value.match(/^\d+\.\d{0,2}/igm);
  }

  if (lang == "de") {
    return value.match(/^\d+\,\d{0,2}/igm);
  } else if (lang == "en") {
    return value.match(/^\d+\.\d{0,2}/igm);
  } else {
    return value.match(/^\d+\.\d{0,2}/igm);
  }
}

In the Stimulus Controller I'm using this code:

let lang = document.documentElement.lang;
let cleaned = ""  
let numberMatch = this.duration_floatTarget.value.match(/^\d+/);
let decimalMatch = decimalMatchFor(lang, this.duration_floatTarget.value)
if (decimalMatch) {
  cleaned = decimalMatch[0]
} else if (numberMatch) {
  cleaned = numberMatch[0]
}
this.duration_floatTarget.value = cleaned;

That works pretty well for me.

Comments

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.