14

I have an input field in which the user should only be able to enter digits [0-9].

document.getElementById("integer").addEventListener('input', restrictToInteger);
function restrictToInteger() {
    this.value = this.value.replace(/[^\d]/g, '');
}
<input type="number" id="integer" />

jsFiddle Demo

The problem is this: When I enter a number (eg. 1234) and then press dot (.), + or - the content of the input field is automatically deleted by the browser (value is set to "" = empty string). But why? Changing the type from number to text seems to fix the problem. But then I lose the up/down arrow functionality of the input field. Any ideas?

Prashant Pokhriyal
  • 3,727
  • 4
  • 28
  • 40
Krisztián Balla
  • 19,223
  • 13
  • 68
  • 84

9 Answers9

7

HTML 4 has an event called onkeypress. With that attribute we can do this without using additional JS:

<input type="number" onkeypress="return (event.charCode == 8 || event.charCode == 0 || event.charCode == 13) ? null : event.charCode >= 48 && event.charCode <= 57">

Here digits from 0 to 9 are allowed using the event.charCode from 48 to 57.

Krisztián Balla
  • 19,223
  • 13
  • 68
  • 84
Chamod Pathirana
  • 713
  • 8
  • 16
4

I think the reason that the browser clean the input value it is because a string with two dots it is not a number.

Some corrections about your code:

You need to change your expression regular if you want to accept number with decimal part. Now, you are only express that you want to accept digits [0-9] and no more chars.

To accomplish want you want, you need to change /[^\d]/g to /[^\d.]/g.

document.getElementById("integer").addEventListener('input', restrictToInteger);

function restrictToInteger()
{
  this.value = this.value.replace(/[^\d.]/g, '');
}
<input type="number" id="integer" />

HOWEVER: If you define your input as number type, the regular expression is not needed. So, you just need to define the input like this and should your to your case:

<input type="number" id="integer" />

[THE SOLUTION]

To fully meet your needs, I came with a solution that catch the keydown event of the input and check if there is any '.' on the input. If yes, I prevent the char to go to the input.

document.getElementById("integer").addEventListener('keydown', restrictToInteger);
var lastCodeWasDot = false;
function restrictToInteger(e)
{
   var inputValue = document.getElementById("integer").value;
   var isDot = false;
   var isDot = (e.keyCode && e.keyCode == 110) || (e.charCode && e.charCode == 190);
   console.log(e.keyCode);
   if(isDot && (inputValue.indexOf(".") > -1 || inputValue == "" || lastCodeWasDot)) {
       e.preventDefault();
   }
   lastCodeWasDot = isDot;
   
}
<input type="number" id="integer" />

Explaning the solution:

The line of code var isDot = (e.keyCode && e.keyCode == 110) || (e.charCode && e.keyCode == 190) || false; is needed because cross browser compatibility.

I don't now why but if you try to get the value from an input number type in the firefox, and if the value finishes with a dot, the value that you will get will be without the last dot of the input. To fix that, I needed to add the variable lastCodeWasDot to fix this issue.

NOTE: The number input can accept floating point numbers, including negative symbols and the e or E character (check out this post)

Ricardo Rocha
  • 14,612
  • 20
  • 74
  • 130
  • @JennyO'Reilly I put a more extensive answer. Please fill free to ask if you have more questions – Ricardo Rocha Sep 28 '17 at 13:18
  • The problem is that simply setting type='number' still allows the user to enter thousands separator sign (eg. the comma) into the field. I want the user only to be able to input digits. Nothing else. – Krisztián Balla Sep 28 '17 at 13:19
  • But you can still enter thousands of dots. Not what OP wants. :-) – frederick99 Sep 28 '17 at 13:49
  • @JennyO'Reilly Now I think this solves your problem. Can you check if I really understand you problem and solve it? – Ricardo Rocha Sep 28 '17 at 13:52
  • In Firefox I can enter anything I want. Even letters. So it does not work for me. :-( – Krisztián Balla Sep 28 '17 at 13:56
  • @JennyO'Reilly Can you test now and check if it is working on firefox as well? – Ricardo Rocha Sep 28 '17 at 13:59
  • Unfortunately it still enables me to enter anything I want in Firefox. – Krisztián Balla Sep 28 '17 at 14:00
  • @JennyO'Reilly Finally I think that I solve the firefox issue. Can you check please if everything is ok now? – Ricardo Rocha Sep 28 '17 at 14:37
  • 1
    @RicardoRocha for me, also using Firefox, I can still enter letters. It doesn't stop me it just adds a red outline (which is pretty cool). – DasBeasto Sep 28 '17 at 18:31
  • @RicardoRocha: Like DasBeasto said, I can still enter letters in Firefox. – Krisztián Balla Sep 29 '17 at 06:56
  • @JennyO'Reilly Sorry about the delay. I made some changes that I Now I think that will really will work. Another think is that the input considers that the char "e" is a number (maybe to represent the exponential number). I will put another solution to only accept dots and numbers bettween [1-9] – Ricardo Rocha Oct 03 '17 at 14:18
  • @RicardoRocha: Thanks for all the effort. I tested the "[THE SOLUTION]" version. In Firefox I can still enter letters into the input field. Btw. I dropped this approach. Now I restrict the input as good as possible and validate the input when the user leaves the field. I also switched from type="number" to type="text", as in that case the browsers don't mess with the entered value. – Krisztián Balla Oct 04 '17 at 07:14
3

Based on the answers of Alexandru-Ionut Mihai and natchiketa I created the following solution:

document.getElementById("integer").addEventListener("input", allowOnlyDigits);

function allowOnlyDigits() {  
  if (this.validity.valid) {
    this.setAttribute('current-value', this.value.replace(/[^\d]/g, ""));
  }
  this.value = this.getAttribute('current-value');
}
<input type="number" id="integer" />

On input the value is checked for validity. If it is valid, all non-digits are removed and the value is stored in a custom attribute of the element. If the value is not valid, the previous value is restored.

Notes:

  • The RegEx-replace is required only for Internet Explorer as it allows you to enter , or . at the end of a number.
  • Tested in IE, Edge, Chrome and Firefox
  • Chrome still allows you to enter a + before and one , after the number.
  • I found one issue: If you initialize the field with a value, the value is lost when you first hit an invalid char on the keyboard.
  • Another issue: You can't enter a negative number.
Krisztián Balla
  • 19,223
  • 13
  • 68
  • 84
2

The only problem was your input type. Change it to text and it should work !

function validate(e) {
    var charCode = e.keyCode? e.keyCode : e.charCode
    if (!(charCode >= 48 && charCode <= 57)) {
        if(!(charCode>=37 && charCode<=40))
            if(charCode!=8 && charCode!=46)
            return false;
    }
}
<input type="number" id="integer" pattern="[0-9]"
onkeydown="return validate(event)"/>
mrid
  • 5,782
  • 5
  • 28
  • 71
2

You can achieve your requirement by copying the old value of input and using setAttribute and getAttribute methods in order to store the values.

function myFunction(input){
  input.setAttribute('current-value',"");
  input.oninput=function(){  
      let currentValue=input.getAttribute('current-value');
      if(input.value!='' || (currentValue>=1 && currentValue<=9))
        input.setAttribute('current-value',input.value);
      input.value=input.getAttribute('current-value');
  }
}
<input type="number" oninput="myFunction(this)"/>
<input type="number" oninput="myFunction(this)"/>
<input type="number" oninput="myFunction(this)"/>
<input type="number" oninput="myFunction(this)"/>
<input type="number" oninput="myFunction(this)"/>
Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
1

I would switch to a cancelable event like keydown.
That way you can prevent the character from being typed in the first place:

var cancelEvent = function (e) {
    e.preventDefault();
    return false;
},
restrictToInteger = function restrictToInteger(e) {
    var acceptableInput = /[0-9]/g,
        clipboardKeys = /[zxcv]/ig,
        field = e.key || e.char,
      isClipboardOperation = (clipboardKeys.test(field) && e.ctrlKey),
        inputIsAcceptable = field ? (
            acceptableInput.test(field)
            || field.length > 1
            || isClipboardOperation
        ) : true;

    if (!inputIsAcceptable) {
        cancelEvent(e);
    }
},
ensureIntegerValueOnPaste = function ensureIntegerValueOnPaste(e) {
 var data = e.clipboardData || e.dataTransfer,
    text = data.getData('text'),
        int = parseInt(this.value + text, 10);

    if (isNaN(int)) {
        cancelEvent(e);
    } else {
        window.setTimeout(function () {
            e.target.value = int;
        }, 0);
    }
},
input = document.getElementById("integer");

input.addEventListener('keydown', restrictToInteger);
input.addEventListener('drop', ensureIntegerValueOnPaste);
input.addEventListener('paste', ensureIntegerValueOnPaste);
<input type="number" id="integer" />

Updated fiddle: https://jsfiddle.net/838pa8hv/2/

Disclaimers:

  • Only tested in Chrome.
  • The test for field.length > 1 is to catch non-numeric keys that are valid as the up/down arrows have a value of ArrowUp and ArrowDown respectively. This also allows for keys like Shift (or Home, Backspace, Delete, etc.) to be pressed as well.

Edit: To handle pastes (and drops), you can do the same thing in those respective events. Updated fiddle and code snippet above.

Edit:
If the expected usability is to be able to paste/drop partial numbers into the field and to not allow negative integers, then you can just change how int is defined in the ensureIntegerValueOnPaste function. Updated fiddle and code snippet above.

pete
  • 24,141
  • 4
  • 37
  • 51
  • I found a bug. :-) You can copy/paste letters into the field. – Krisztián Balla Sep 29 '17 at 07:22
  • @JennyO'Reilly Updated my answer. Hope it helps. – pete Sep 29 '17 at 13:34
  • Hey, thanks! If I enter "1" I can't paste a "2" behind it. Paste seems to overwrite the entire content. Is this intended? – Krisztián Balla Sep 29 '17 at 13:58
  • @JennyO'Reilly I would consider that intended behavior as I don't think most people are not going to type a portion of the number and then paste or drop in the rest. Typically, I would think they'll either paste/drop or just type the number in. You can change the behavior to allow partial pasting/dropping though by *removing the entire falsy clause* from the `isNaN(int)` conditional. I didn't like that as `-` could wind up in the middle or the end of the field and that... seemed silly. – pete Sep 29 '17 at 14:08
  • I understand. Nevertheless the expected usability is exactly that: pasting a portion of the number. One thing though: You can't enter a negative value, can you? (however the question originally didn't ask for it) – Krisztián Balla Sep 30 '17 at 15:45
  • When you paste something into the field, it is always added to the end. That's a strange behaviour I think. – Krisztián Balla Oct 01 '17 at 10:14
1

When you call oninput, the <input> element first calls its internal methods to handle the value. This prevents your function from seeing any actual erroneous characters, namely e+-. - all used by JavaScript to format numbers.

You can see this by adding console.log calls before and after changing this.value.

console.log(this.value);
this.value=this.value.replace(/[^\d]/g, '');
console.log(this.value);

There is never any difference!

If you try, for example:

console.log(this.value);
this.value+=1; // or *=2 for numerical fun
console.log(this.value);

you can see a difference.

So your function is hastening the normal internal calls <input type='number'/> would normally make when handling illegal input.

Can't quite see why the field is left blank and not 1 though.

JMP
  • 4,417
  • 17
  • 30
  • 41
0

You don't need regular expression, you can use parseFloat() function. Your input type remains unchanged, there are still "arrows" to increase/decrease number and also it makes sure that your input will not start with zero.

document.getElementById("integer").addEventListener('input', restrictToInteger);
function restrictToInteger() {
  this.value = parseFloat(this.value);
}
<input type="number" id="integer" />
Patrik Krehák
  • 2,595
  • 8
  • 32
  • 62
  • This does not work as we already found out with the help of the others that the browser automatically clears the value of the field if it considers it to be not a valid number. – Krisztián Balla Sep 28 '17 at 13:41
0

You have to check if the value is not a number and then stop user.

document.getElementById("integer").addEventListener('input', restrictToInteger);

function restrictToInteger(e)
{
  if(isNaN(e.data)){
     alert("only numbers allowed");
  }
}
<input type="number" id="integer" />
Krisztián Balla
  • 19,223
  • 13
  • 68
  • 84
Ravi Kumar
  • 58
  • 1
  • 7