5

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>
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
Anshuma
  • 3,242
  • 2
  • 16
  • 14

7 Answers7

10

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.

Hubert Grzeskowiak
  • 15,137
  • 5
  • 57
  • 74
  • 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. – Wiktor Stribiżew Feb 01 '17 at 14:18
  • 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. – Hubert Grzeskowiak Feb 01 '17 at 15:16
  • You need to store the place of the cursor, and then put it back. I once tried that way, and it involved much code :( – Wiktor Stribiżew Feb 01 '17 at 15:19
  • 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. – Hubert Grzeskowiak Feb 01 '17 at 15:44
  • Thanks a ton @HubertGrzeskowiak – Anshuma Feb 02 '17 at 11:23
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>
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • 1
    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. – Anshuma Feb 01 '17 at 13:35
  • This fails on drag'n'drop and copy'n'paste. It also breaks keyboard-based copy'n'paste. – Hubert Grzeskowiak Feb 01 '17 at 13:53
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.

Octav Zlatior
  • 451
  • 4
  • 14
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

Ofir Baruch
  • 10,323
  • 2
  • 26
  • 39
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));
    }
    
Ifeanyi Amadi
  • 776
  • 5
  • 10
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); }
});
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.

Robert Reiz
  • 4,243
  • 2
  • 30
  • 43