89

Using <input type=number> will cause this.value inside of an event listener to return an empty string if the input is not a valid number. You can see an example of this at http://jsfiddle.net/fSy53/

However, the invalid characters are still displayed in the input.

Is there any way to get the value that is actually displayed, including the invalid characters, from within an event listener?

My ultimate goal is to prevent users from actually typing any non-numeric characters into the field. I need to use type=number so that the numeric virtual keyboard is used by mobile devices. My goal would be to do something like this.value = this.value.replace(/[^0-9.]/g, "") on keyup keypress, but this doesn't work because if an invalid character is typed, reading from this.value returns "".

Explosion Pills
  • 188,624
  • 52
  • 326
  • 405
  • 2
    I think this was asked before and the answer was no, let me dig it up. **EDIT: here is is: http://stackoverflow.com/questions/3090810/getting-the-value-of-a-html5-number-input** – Qantas 94 Heavy Nov 13 '13 at 23:21
  • About the numeric keyboard: There is a new HTML5 attribute `inputmode` that could be set to `numeric` to show a numeric keyboard. Unfortunately no browser supports it yet, according to [mozilla](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input) – some Nov 13 '13 at 23:24
  • Note that the issue described happens in Firefox, but not in Chrome. Chrome will prevent non-numeric characters from appearing in an input if `type='number'` is set. – Sean the Bean Oct 31 '16 at 16:18
  • Update regarding the `inputmode` attribute: it is still not supported in any browser by default (see http://caniuse.com/#search=inputmode), but can optionally be enabled in Firefox by setting the `dom.forms.inputmode` flag. – Sean the Bean Oct 31 '16 at 16:23
  • hmmm... isnt "e" the only "invalid" character thats displayed in an `type=number`?? – oldboy Jul 20 '19 at 23:41
  • Does this answer your question? https://stackoverflow.com/a/62070692/1692889 – Samuel Charpentier May 28 '20 at 18:47
  • 1
    Preventing users from entering a non-numeric character (i.e. BREAKING their keyboard) is not very user friendly. A user friendly approach is to allow them to type and explain what is wrong. A user that is trying to enter a string into a number input will not understand why his keyboard stopped working. I'm surprised how many developers strive to implement this anti user feature. This goes to all blocking behaviors (max-length, patterns), very non user friendly. Not to mention that users do not usually look at their screen while typing. – Cesar Feb 23 '21 at 21:59
  • @Cesar, I agree. But what about submit-blocking specifically? The `pattern` is not an input-blocking behavior; a `pattern` only blocks in upon submit (in Chrome at least). And some input-blocking is browser-controlled; the `min/max` and `min/max-length` fields do block input, a possible accessibility problem that is on the browser to fix. – Wesley B Sep 28 '22 at 22:14

15 Answers15

92

Try preventing the default behaviour if you don't like the incoming key value:

document.querySelector("input").addEventListener("keypress", function (evt) {
    if (evt.which < 48 || evt.which > 57)
    {
        evt.preventDefault();
    }
});
Glenn Lane
  • 3,892
  • 17
  • 31
  • `keypress` event is only fired on the document. You'd need to setup a keyboard manager for this – megawac Nov 13 '13 at 23:33
  • 8
    Not true. keypress fires on the input element, and bubbles to document – Glenn Lane Nov 13 '13 at 23:34
  • 1
    @megawac do you have a citation for that? That's contrary to my experience. The OP stated that preventing non-numeric contents is the goal, I think this is the right approach: prevent it before it happeans and don't worry about the .value – Mike Edwards Nov 13 '13 at 23:35
  • 1
    @GlennLane the problem is that for shift keys such as `$` the `.which` is the same as for the non-shift key and on the virtual keyboards, `e.shiftKey` is not set to a truthy value. Related: http://stackoverflow.com/questions/19964074/keydown-allowing-numbers-but-not-shift-symbols-on-iphone – Explosion Pills Nov 13 '13 at 23:37
  • I don't find that behaviour on my browser... $ -> 36, 4 -> 52. Must be a Chrome thing. – Glenn Lane Nov 13 '13 at 23:41
  • @GlennLane by virtual keyboards I mean on like an iPhone or something. Even if it were a Chrome thing, that's no good. I need to support more browsers than just the one your solution works for – Explosion Pills Nov 13 '13 at 23:43
  • The issue was using `keyup` -- it records the same key code (or `which` or whatever it is) based on the key typed. Keypress does seem to work consistently. – Explosion Pills Nov 14 '13 at 14:37
  • be aware that this blocks "tab" as well in firefox, but not in chrome – ulilicht Aug 24 '15 at 09:47
  • 5
    With this you can't press arrow, backspace etc keys, there is extended solution exists http://stackoverflow.com/a/995193/691194 – azhidkov Dec 03 '15 at 14:55
  • if (isNaN(parseInt(evt.key,10))) { evt.preventDefault(); } – EugenSunic Aug 23 '19 at 07:10
  • Unable to backspace with your solution, @EugenSunic – Ralph David Abernathy Nov 23 '20 at 18:53
  • oninput ------- better to use – Strywyr Feb 28 '23 at 08:53
26

You can accomplish this by preventing the keyPress event from occurring for non-numeric values

e.g (using jQuery)

$('.input-selector').on('keypress', function(e){
  return e.metaKey || // cmd/ctrl
    e.which <= 0 || // arrow keys
    e.which == 8 || // delete key
    /[0-9]/.test(String.fromCharCode(e.which)); // numbers
})

This accounts for all different types of input (e.g. input from the number pad has different codes than the keyboard) as well as backspace, arrow keys, control/cmd + r to reload etc

Redithion
  • 986
  • 1
  • 19
  • 32
dlangevin
  • 354
  • 3
  • 7
18

Please note that e.which, e.keyCode and e.charCode are deprecated: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/which

I prefer e.key:

document.querySelector("input").addEventListener("keypress", function (e) {
    var allowedChars = '0123456789.';
    function contains(stringValue, charValue) {
        return stringValue.indexOf(charValue) > -1;
    }
    var invalidKey = e.key.length === 1 && !contains(allowedChars, e.key)
            || e.key === '.' && contains(e.target.value, '.');
    invalidKey && e.preventDefault();});

This function doesn't interfere with control codes in Firefox (Backspace, Tab, etc) by checking the string length: e.key.length === 1.

It also prevents duplicate dots at the beginning and between the digits: e.key === '.' && contains(e.target.value, '.')

Unfortunately, it doesn't prevent multiple dots at the end: 234....

It seems there is no way to cope with it.

Nrzonline
  • 1,600
  • 2
  • 18
  • 37
pavok
  • 380
  • 3
  • 10
  • 2
    I like this approach. It is clean and works well. One issue with it is that it blocks things like `Ctrl+R` (reload current page), `Ctrl+A`, etc. I believe it is safe to add an `!e.ctrlKey` check. – mfulton26 Jul 31 '17 at 14:45
  • 1
    The keypress event is also deprecated. See https://developer.mozilla.org/en-US/docs/Web/API/Document/keypress_event and also https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event – Raikish Mar 30 '21 at 13:24
8

The other answers seemed more complicated than necessary so I adapted their answers to this short and sweet function.

function allowOnlyNumbers(event) {
  if (event.key.length === 1 && /\D/.test(event.key)) {
    event.preventDefault();
  }
}

It won't do change the behavior of any arrow, enter, shift, ctrl or tab keys because the length of the key property for those events is longer than a single character. It also uses a simple regular expressions to look for any non digit character.

KenCorbettJr
  • 196
  • 1
  • 3
3
inputs[5].addEventListener('keydown', enterNumbers);

function enterNumbers(event) {
  if ((event.code == 'ArrowLeft') || (event.code == 'ArrowRight') ||
     (event.code == 'ArrowUp') || (event.code == 'ArrowDown') || 
     (event.code == 'Delete') || (event.code == 'Backspace')) {
     return;
  } else if (event.key.search(/\d/) == -1) {
    event.preventDefault();
  }
}

in this case, the value of the input field stays intact when a non-number button is pressed, and still delete, backspace, arrowup-down-left-right work properly and can be used for modifying the digital input.

Fevralle
  • 31
  • 1
3

This solution seems to be working well for me. It builds on @pavok's solution by preserving ctrl key commands.

document.querySelector("input").addEventListener("keypress", function (e) {
  if (
    e.key.length === 1 && e.key !== '.' && isNaN(e.key) && !e.ctrlKey || 
    e.key === '.' && e.target.value.toString().indexOf('.') > -1
  ) {
    e.preventDefault();
  }
});
pigeontoe
  • 446
  • 3
  • 8
2

try this one :

  1. current key pressed -> e
  2. if e is not a number -> isNaN(e.key)
  3. when condition is true , so default action should not be taken as it normally would be -> e.preventDefault()
  4. else, return default action... e
input.addEventListener("keypress", function (e) {
  if (isNaN(e.key)) e.preventDefault();
});
  • Could you please explain what your code does and how it solves the OP's Issue? – Alexander Apr 30 '22 at 22:40
  • any key pressed is equal to (e) parameter , then if e.key is not a number , return preventDefault() , so what is preventDefault() method ? ....tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be. – farid teymouri May 01 '22 at 19:06
  • Thank you for responding, but I actually was hoping you would edit your answer with those details to improve the quality of your post. You will notice that when a user answers a question with 'try this' and a few lines of code, the answers rarely perform well – Alexander May 01 '22 at 19:19
  • you right , sorry. – farid teymouri May 01 '22 at 22:22
1

I will add MetaKey as well, as I am using MacOS

input.addEventListener("keypress", (e) => {
    const key = e.key;
    if (!(e.metaKey || e.ctrlKey) && key.length === 1 && !/\d\./.test(key)) {
        e.preventDefault();
    }
}

Or, you can try !isNaN(parseFloat(key))

Polv
  • 1,918
  • 1
  • 20
  • 31
1

Update on the accepted answer:

Because of many properties becoming deprecated

(property) KeyboardEvent.which: number @deprecated

you should just rely on the key property and create the rest of the logic by yourself:

The code allows Enter, Backspace and all numbers [0-9], every other character is disallowed.

document.querySelector("input").addEventListener("keypress", ({ key, preventDefault }) => {
  if (isNaN(parseInt(key, 10)) && !['Backspace', 'Enter'].includes(key)) {
      preventDefault();
    }
});

NOTE This will disable paste action

EugenSunic
  • 13,162
  • 13
  • 64
  • 86
0

Based on Nrzonline's answer: I fixed the problem of the multiple "." at the end of the input by adding a

let lastCharacterEntered

outside of the input and then onKeyPress

e => {
          var allowedChars = "0123456789.";
          function contains(stringValue, charValue) {
            return stringValue.indexOf(charValue) > -1;
          }
          var invalidKey =
            (e.key.length === 1 && !contains(allowedChars, e.key)) ||
            (e.key === "." && contains(e.target.value, "."));
          console.log(e.target.value);
          invalidKey && e.preventDefault();
          if (!invalidKey) {
            if (lastCharacterEntered === "." && e.key === ".") {
              e.preventDefault();
            } else {
              lastCharacterEntered = e.key;
            }
          }
        }
Chajia
  • 21
  • 4
0

I just had the same problem and discovered an alternative solution using the validation API - works without black magic in all major browsers (Chrome, Firefox, Safari) except IE. This solution simply prevents users from entering invalid values. I also included a fallback for IE, which is not nice but works at least.

Context: onInput function is called on input events, setInputValue is used to set the value of the input element, previousInputValue contains the last valid input value (updated in setInputValue calls).

    function onInput (event) {
        const inputValue = event.target.value;

        // badInput supported on validation api (except IE)
        // in IE it will be undefined, so we need strict value check
        const badInput = event.target.validity.badInput;

        // simply prevent modifying the value
        if (badInput === true) {
        // it's still possible to enter invalid values in an empty input, so we'll need this trick to prevent that
            if (previousInputValue === '') {
                setInputValue(' ');
                setTimeout(() => {
                    setInputValue('');
                }, 1);
            }
            return;
        }

        if (badInput === false) {
            setInputValue(inputValue);
            return;
        }

        // fallback case for IE and other abominations

        // remove everything from the string expect numbers, point and comma
        // replace comma with points (parseFloat works only with points)
        let stringVal = String(inputValue)
            .replace(/([^0-9.,])/g, '')
            .replace(/,/g, '.');

        // remove all but first point
        const pointIndex = stringVal.indexOf('.');
        if (pointIndex !== -1) {
            const pointAndBefore = stringVal.substring(0, pointIndex + 1);
            const afterPoint = stringVal.substring(pointIndex + 1);

            // removing all points after the first
            stringVal = `${pointAndBefore}${afterPoint.replace(/\./g, '')}`;
        }

        const float = parseFloat(stringVal);
        if (isNaN(float)) {
            // fallback to emptying the input if anything goes south
            setInputValue('');
            return;
        }
        setInputValue(stringVal);
}
Ábel Énekes
  • 237
  • 1
  • 10
0

Prevent typing non-Alphabet in specific input id for pages that have more than one input item.it's usable for Oracle Apex developers --- Append in HTML header of page

<script type="text/javascript">
function validateNumber(event) {
  const activeElmnt = document.activeElement;  
  var keyCode = event.keyCode;
  var excludedKeys = [8, 37, 39, 46];
 
   if ( activeElmnt.id == "input id in HTML page"){
        if (!((keyCode >= 65 && keyCode <= 90) ||
            (keyCode >= 97 && keyCode <= 122) ||
            (excludedKeys.includes(keyCode)))) {
            console.log("alphabets are not allowed");
            event.preventDefault();

  }
}
  console.log("keycode: " + keyCode + "ActiveElemet: "+activeElmnt.id);
}
</script>

-- Append in page HTML Body attribute onkeydown="validateNumber(event);"

ZASA82
  • 11
  • 2
0

here is my simple solution simply in this solution u should keep the input type to text so when you use event.target.value you get the full string not only the numbers or an empty string instead so you actually can check if there is a point yet or not. and i didn't use the deprecated properties event.which

isNumber(event) {
      var allowed = "";
      if (event.target.value.includes(".")) {
        allowed = "123456789";
      } else {
        allowed = "123456789.";
      }
      if (!allowed.includes(event.key)) {
        event.preventDefault();
      }
    }

document.getElementById('number').addEventListener('keypress', isNumber);

the HTML 

```

 function isNumber(event) {
      var allowed = "";
      if (event.target.value.includes(".")) {
        allowed = "0123456789";
      } else {
        allowed = "0123456789.";
      }
      if (!allowed.includes(event.key)) {
        event.preventDefault();
      }
      }
      
      document.getElementById('number').addEventListener('keypress',isNumber);
<h3>You can input only numbers and one point (this would be a float or int) </h3>
<input type="text" id="number" />
0

This one should work. Only works with integers.

function validateNumberInput(e) {
    if (isNaN(e.key)) {
        e.preventDefault();
    }
}

You can however implement floats with few more lines:

function validateNumberInput(e) {
    if (isNaN(e.key) && e.key !== '.') {
        e.preventDefault();
    } else if (e.key === '.') {
        if (e.target.value.indexOf('.') >= 0) {
            e.preventDefault(); 
        }
    }
}

Finally use it like:

targetInput.addEventListener('keypress', function (e) {
    validateNumberInput(e);
});
Sajidur Rahman
  • 2,764
  • 1
  • 27
  • 26
-2

Try it:

document.querySelector("input").addEventListener("keyup", function () {
   this.value = this.value.replace(/\D/, "")
});
Duke
  • 3,226
  • 1
  • 18
  • 23
  • 1
    In the event the input contains characters the value is cleared and not just the non-digit characters. This is because the value of a numeric box is a floating point number. Therefore (according to the spec) a non-floating point will set the value of the control to ''. – Nico Nov 13 '13 at 23:30