I have a html <input> element that I want to accept only numbers and to be recognised on mobile devices as a number field. I also want invalid characters to be swallowed, just like for standard type=number swallowing disallowed characters.
I've tried the obvious type=number but it has a number of shortcomings. Specifically it allows 'e', '+' and '-' (at least in chrome), but these were easy to fix with some JS. The real problem is with the '.' character, I want to be able to enter floating point numbers e.g. '0.10', '5.5054', but don't want to be able to enter invalid strings like '0.10.1' for instance. I tried to resolve this by allowing only 1 '.' at a time but this failed as the input.value gets massaged by the browser e.g. '5.' becomes '5', '5..' becomes null (!) and it seems impossible to get the raw string value typed in the input. The above means checking for existing '.'s and taking action appears to be a dead end...
Core questions:
Is there a way I missing to inspect and conform the input?
'Is there a way of marking an input as a number without the logistical baggage of type=number?
Note:
* I realise that you can paste whatever you want in, I consider that behaviour pathological and shouldn't be covered by input prevention.
Update
To clarify, I have already tried keypress, keydown etc events and they aren't adequate as I want to see how many '.'s exist in the input currently to choose whether or not to allow another. At this point the input.value has been massaged by the browser to remove '.'s. I want to conditionally allow characters based on the current number of '.'s that have been entered.
Example
HTML (angular style binding for brevity)
<input type="number" (keydown)="keyDown()">
JS
function keyDown($event: KeyboardEvent) {
const inputField = // obtain reference to input element
const value = inputField.value;
if ( value.indexOf('.') !== -1 && $event.key === '.') { // disallow another . if one is present
// ! input field prunes . so this check isn't sufficient
$event.preventDefault();
return;
}
// This is the crux of the problem e.g.
// type 5. into input field, value === 5
// type 5.. into the input field, value === null
// Since the . char is removed by the input element there's no way to know how many are present!
console.log(value);
}
Summary
Is there a way to signal that an <input> is of type number without using the type=number attribute setting.
i.e. mobile devices recognise and display number pad etc
For an <input> that has type=number is there a way to swallow all key input that doesn't result in a valid number
Before the character is added to the input by the browser, no janky deletion on keyup
Is there a way to signal that an <input> is of type number without using the type=number attribute setting.
i.e. mobile devices recognise and display number pad etc
Use inputmode="decimal" instead of type="number" to signal a mobile device to use a number pad keyboard input. This way you can continue to use type="text" and process the input as needed.
See MDN for more info and inputtypes.com to test on a device.
A slightly different approach. It allows digits, only 1 period, and backspace. All the rest of KeyboardEvent.keys including ctrl + v and ctrl + c are ignored. But if wish to allow them, you can do so.
To check if the character is one of the 10 digits, I am using event.key since they can have two different codes: Digits[0-9] and Numpad[0-9]. But for the period and backspace, I am using event.code since they have only one code.
const input = document.querySelector("#number_input");
const App = {
isDigit: function(key) {
const digits = [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9"
];
return digits.includes(key);
},
isPeriod: function(code) {
return code === "Period";
},
isBackSpace: function(code) {
return code === "Backspace";
},
handleEvent: function(event) {
const key = event.key;
const code = event.code;
const value = input.value;
if (App.isDigit(key) || App.isPeriod(code) || App.isBackSpace(code)) {
if (App.isPeriod(code) && value.indexOf(key) !== -1) {
event.preventDefault();
}
} else {
event.preventDefault();
}
}
};
input.onkeydown = App.handleEvent
<input id="number_input" />
A clever hack
Since you insist to use a number input. First use, a dummy text input which you can hide it using either CSS or Js and validate its value instead of the number input.
const input = document.querySelector("#number_input");
const dummyInput = document.querySelector("#dummy_input")
const App = {
isDigit: function(key) {
const digits = [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9"
];
return digits.includes(key);
},
isPeriod: function(code) {
return code === "Period";
},
isBackSpace: function(code) {
return code === "Backspace";
},
handleEvent: function(event) {
const key = event.key;
const code = event.code;
const dummyValue = dummyInput.value;
if (App.isBackSpace(code)) {
dummyInput.value = dummyValue.substring(0, dummyValue.length - 1)
} else {
if (App.isDigit(key) || App.isPeriod(code)) {
if (App.isPeriod(code) && dummyValue.indexOf(key) !== -1) {
event.preventDefault();
} else {
dummyInput.value += event.key
}
} else {
event.preventDefault();
}
}
}
};
input.onkeydown = App.handleEvent
<input type="number" id="number_input" />
<input type="text" id="dummy_input" />
Update
All of the answers that use input[type="number"] have a problem. You can change the input's value to a negative number by mouse wheel/spinner. To fix the issue, set a minimum value for the input.
<input type="number" min="1" id="number_input" />
You need to listen for onchange events and then change value of the dummy input.
const input = document.querySelector("#number_input");
const dummyInput = document.querySelector("#dummy_input")
const App = {
isDigit: function(key) {
const digits = [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9"
];
return digits.includes(key);
},
isPeriod: function(code) {
return code === "Period";
},
isBackSpace: function(code) {
return code === "Backspace";
},
handleEvent: function(event) {
const key = event.key;
const code = event.code;
const dummyValue = dummyInput.value;
if (App.isBackSpace(code)) {
dummyInput.value = dummyValue.substring(0, dummyValue.length - 1)
} else {
if (App.isDigit(key) || App.isPeriod(code)) {
if (App.isPeriod(code) && dummyValue.indexOf(key) !== -1) {
event.preventDefault();
} else {
dummyInput.value += event.key
}
} else {
event.preventDefault();
}
}
},
handleChange: function(event) {
dummyInput.value = event.target.value
}
};
input.onkeydown = App.handleEvent;
input.onchange = App.handleChange;
<input type="number" min="1" id="number_input" />
<input type="text" id="dummy_input" />
trigger special function for onkeypress and check if your allowed character are typed or not. If not - prevent the default behavior.
let special = document.getElementById('special_input');
special.addEventListener("keypress", function(e) {
let dot = 46;
// allowed char: 0-9, .
let allow_char = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, dot];
if (allow_char.indexOf(e.which) !== -1) {
// only 1 dot
if (e.which == 46 && special.value.indexOf('.') !== -1)
e.preventDefault();
} else {
e.preventDefault();
}
});
<input id='special_input'>
updated
The answer is updated as per requirement changes.
okay I try to fix one more problem and that is paste
according to following code you can not paste anything, you can only paste numbers, if you try to paste string, the input box will become empty automatically :)
let special = document.getElementById('inputField');
special.addEventListener("keypress", function(e) {
let dot = 46;
// allowed char: 0-9, .
let allow_char = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, dot];
if (allow_char.indexOf(e.which) !== -1) {
// only 1 dot
if (e.which == 46 && special.value.indexOf('.') !== -1)
e.preventDefault();
} else {
e.preventDefault();
}
});
function checkString()
{
setTimeout(() => {
var value = document.getElementById("inputField").value;
value = parseInt(value);
if(isNaN(value))
document.getElementById("inputField").value = "";
}, 100);
}
<input type="number" id="inputField" onpaste="checkString()"/>
Check truthy with
value == parseFloat(value)
parseFloat() will capture the first decimal point and remove any additional ones.
Using '==' instead of '===' will make sure the reference value isn't changed.
function keyDown($event: KeyboardEvent) {
const inputField = // obtain reference to input element
const value = inputField.value;
if ( value == parseFloat(value)) { // disallow another . if one is present
// ! input field prunes . so this check isn't sufficient
$event.preventDefault();
return;
}
// This is the crux of the problem e.g.
// type 5. into input field, value === 5
// type 5.. into the input field, value === null
// Since the . char is removed by the input element there's no way to know how many are present!
console.log(value);
}
Here is a similar test you can run in node.js to verify it accomplishes what you are going for.
function numCheck (value) {
if (value == parseFloat(value)) {
console.log(value)
} else {
console.log('not a number')
}
}
numCheck("5");
numCheck("5.5");
numCheck("5.5.5");
numCheck("A");
For restrictions, Regex is best way to go. Register event listener on keypress
numbersOnly(event) {
const numbers = /^([0-9])$/;
const result = numbers.test(event.key);
return result;
}
Above function will swallow charset, if it doesn't match regex
Javascript Regex: validating a double/float
Above link has regex, who allows double/float. Hope it helps :)
This may be what your looking for:
This input eats all caracters exept for numbers and "."
let prev = "";
function keyUp(event) {
if ((!event.target.value && event.keyCode !== 8) || event.target.value.indexOf('e') !== -1 || parseFloat(event.target.value) == NaN) {
event.target.value = prev;
} else {
prev = event.target.value;
}
}
<input type="number" onkeyup="keyUp(event)">
Perhaps an indirect solution to your problem. You can look into validity property to determine if the input is valid.
You can check the validity without the need to access the value, which may not be returned in case of invalid input.
HTML:
<input type="number" id="num" step="0.1"/>
JS:
var numField = document.getElementById('num');
numField.addEventListener("keyup", event => {
if(!numField.validity.valid)
{
alert('invalid input');
}
});
Some tests:
34 -> valid
3.4 -> valid
e -> invalid
+4 -> valid
3.4. -> invalid
3.4.. -> invalid
.4 -> valid (evaluates to 0.4)
3e+2 -> valid
1e -> invalid
eee -> invalid
-0.3e+1 -> valid
..32e.. -> invalid
This works both with pasting and inputting the values.
UPDATE:
The original question is a bit verbose. I am trying to break it down and answer part by part.
Input[type="number"] allows access to the value only if it is valid and returns empty otherwise. Thus, you can know if value is correct or not, however you cannot get the invalid value.
You could get around this by pushing individual keypresses into a buffer. However, maintaining sync between the buffer and actual value is challenging due to copy/paste, input field swallowing certain keys, moving the cursor around and inserting keys, etc. Implementing reliable cross-platform support for all of this seems like overkill. A better approach would be to implement other means to input the number, e.g. a slider. This resource covers the basics of designing number inputs
So, you are left with knowing if the current input is valid and the last key pressed. The problem is that certain invalid inputs, e.g. "4." may lead to a valid input "4.1". Thus, just swallowing the last key in case the input turns invalid is out of the question.
You could implement a rule that if invalid input is not turned into a valid one within 2 keystrokes (or 1 second) the input returns to last valid state swallowing the invalid input. However, I would argue that this creates UX problems because users may not be used to such behavior and it opens doors for users submitting technically valid, yet unwanted values.
Core questions:
Is there a way I missing to inspect and conform the input?
Is there a way of marking an input as a number without the logistical baggage of type=number?
Apparently not.
Yes, there is by rethinking the way how you enter numeric values.
I am doing a decimal number input validation where I want to prevent typing 'point' for the second consecutive time
For example-
Above in the input box after keypress of the first period, I shouldn't be able to keypress it for the second consecutive time.
I have done this so far-
if (e.which === 190) {
if(value === '') {
e.preventDefault()
this.setState({[error]: "Only one period allowed in decimal numbers!"});
return false;
}
}
But this only prevents the 3rd keypress and allows the second period.
How do I prevent the keypress using JavaScript?
PS- I am using input field if type- 'number'. So the after I press the second dot the e.target.value becomes null.
You can use regular expression alongwith onChange event.
testHanlder = (event) => {
let value = event.target.value;
if (value) {
value = value.replace(/\.+/g, ".")
this.setState({value})
}
}
Even if it passes the second decimal number issue, it will still allow alphabets inside the input field.
Instead you could use:-
var textBox = document.getElementById("txt")
textBox.addEventListener("keyup",function(e) {
var floatValue = parseFloat(textBox.value) || ""
if(floatValue !== textBox.value) textBox.value = floatValue
})
I need a field which can only take numbers, but not allow for signs such as "+", "-", "*" and "/". 0 can also not be the first number. If I make an Input field and set it's type to "number" I'm still allowed to write at least "+" and "-", and I can't quite seem to prevent the user from writing 0 as the first number either.
$('input#update-private-ext').on('keydown', function (e) {
var value = String.fromCharCode(e.keyCode);
if ($(this).text.length == 0 && value == 0) {
return false;
}
});
The above was my first attempt at making the function disallow 0 as the first character, but it doesn't seem to work. It just lets me write 0 as the first character. I also tried this to stop the signs from showing up:
$('input#update-private-ext').on('keydown', function (e) {
var badChars = '+-/*';
var value = String.fromCharCode(e.keyCode);
if ($(this).text.length == 0 && value == 0) {
return false;
}
if (badChars.indexOf(value) == -1) {
return false;
}
});
But with the badChars check, I cannot write anything in my field. So what am I missing here?
You should use e.key to get the current key pressed. String.fromCharCode(e.keyCode) gives the wrong result.
Also you should check if the bad chars is not -1. If it is, then your char is not a bad character and so you should not enter the if.
If you want to get the length of the input field you should use jQuery's .val() and not .text(). Or you can simply do it without jQuery using this.value.length.
$('input#update-private-ext').on('keydown', function (e) {
var badChars = '+-/*';
var value = e.key;
if (this.value.length == 0 && value == '0') {
return false;
}
if (badChars.indexOf(value) !== -1) {
return false;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="update-private-ext">
When you compare numbers and strings you must remember that numbers are encoded by using character codes from 48 to 57 and comparing strings with numbers is error-prone in JavaScript as there are many implicit coercions. You should be comparing objects of the same type to avoid the confusion.
In your case, the comparison should be done in the way that parsed string from the String.fromCharCode equals '0' - zero character (string), not the 0 as a number.
There are also issues of the keyCode parsing which yield strange values for the symbols because you would have to manually consider if Shift and other meta keys are pressed when parsing. Save yourself a trouble and just use e.key to get parsed key value.
By the way, please see the difference between this and $(this). Basically, in your case, it means that real instance of the input field is the first element of JQuery iterator - $(this)[0]. You may then just use this, which is automatically set to the target element in the event handler.
Please see the following example of blocking first 0 with debug information printed out:
$('input#update-private-ext').on('keydown', function (e) {
var value = e.key;
console.log('Typed character:');
console.log(value);
console.log('$(this)');
console.log($(this));
console.log('this (input element):');
console.log(this);
console.log("input's value:");
console.log(this.value);
if (this.value.length == 0 && value == '0') {
console.log('blocked');
return false;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="update-private-ext" />
In order to block other characters you can just filter them the following way (remember that indexOf returns -1 when the index is not found):
$('input#update-private-ext').on('keydown', function (e) {
var badChars = '+-/*';
var value = e.key;
if (this.value.length == 0 && value == '0') {
return false;
}
//Please note NOT EQUALS TO -1 which means not found.
if (badChars.indexOf(value) !== -1) {
return false;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="update-private-ext" />
You can do something like this below:
1. Check for bad chars if badChars.indexOf(v) >= 0.
2. Disallow starting from 0 by checking if the input starts from 0 and if yes, set the input field to blank.
This can give you a start!
$('input#update-private-ext').on('keydown', function(e) {
var badChars = '+-/*';
var v = e.key;
if (badChars.indexOf(v) >= 0) {
return false;
}
if ($(this).val().startsWith('0')) {
$(this).val("");
return false;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="update-private-ext" />
I called a class called test for my textbox. When I entered the first value for e.g. the first value as 4., then suddenly the output coming as 4.00. I just want to restrict entry only for two decimal places.
$(".test").keyup(function (event) {
debugger;
this.value = parseFloat(this.value).toFixed(2);
});
This small change to your code may suffice:
this.value = this.value.replace (/(\.\d\d)\d+|([\d.]*)[^\d.]/, '$1$2');
Essentially replace the decimal point followed by any number of digits by a decimal point and the first two digits only. Or if a non digit is entered removes it.
What about something like this:
$(".test").keyup(function (event) {
if ((pointPos = this.value.indexOf('.')) >= 0)
$(this).attr("maxLength", pointPos+3);
else
$(this).removeAttr("maxLength");
});
Here is a working fiddle.
you can use the maxLength attribute for that, try
$(".test").keyup(function (event) {
var last = $(this).val()[$(this).val().length - 1];
if (last == '.') {
$(".test").attr("maxlength", $(this).val().length+2);
}
});
You shouldn't worry about what the user has in the input until they submit the form. You really don't care what's in there before then. However, if you want to warn about invalid input, you can put a message on the screen if you detect non–conforming input, e.g.
<script>
function validate(element) {
var re = /^\s*\d*\.?\d{0,2}\s*$/;
var errMsg = "Number must have a maximum of 2 decimal places";
var errNode = document.getElementById(element.name + '-error')
if (errNode) {
errNode.innerHTML = re.test(element.value)? '' : errMsg;
}
}
</script>
You should probably also put a listener on the change handler too to account for values that get there by other means.
$(document).on("keyup", ".ctc", function ()
{
if (!this.value.match(/^\s*\d*\.?\d{0,2}\s*$/) && this.value != "") {
this.value = "";
this.focus();
alert("Please Enter only alphabets in text");
}
});
I have a JavaScript function that validates an input field and prevents the user from typing anything that doesn't match the condition. This function is based on event.keyCode.
I'm trying to modify the function to use a RegExp and validates not "per character" but "per whole input" so that it does the same, but with different conditions:
numeric only
allowed decimal "." or ","
Here is the function in its current form, using event.keyCode:
function isNumeric(evt, alertDIVid, alertMsg) {
var charCode = (evt.which) ? evt.which : event.keyCode
if (charCode >= 48 && charCode <= 57) {
document.getElementById(alertDIVid).innerHTML = '';
return true;
}
else {
document.getElementById(alertDIVid).innerHTML = alertMsg;
return false;
}
}
document.getElementById('AMNT').onkeypress = function(event) {
event = event || window.event;
return isNumeric(event, 'numericalert', 'Numeric values only!')
};
In order to do the kind of validation you want, you need to listen to the keyup event instead. This event fires after the field is changed, so that you know the new value of the field. You also need to know the previous value of the field so you can "reset" it if what the user typed turns out to be invalid.
For example:
(function() {
var previousValue = document.getElementById('myInput').value;
var pattern = /^\d*((\.|,)\d*)?$/;
function validateInput(event) {
event = event || window.event;
var newValue = event.target.value || '';
if (newValue.match(pattern)) {
// Valid input; update previousValue:
previousValue = newValue;
} else {
// Invalid input; reset field value:
event.target.value = previousValue;
}
}
document.getElementById('myInput').onkeyup = validateInput;
}());
Working demo: http://jsfiddle.net/8kUdG/
It's worth noting that this will also validate empty strings, as well as unfinished numbers, like 5, or 42. (otherwise the user would have to insert the decimal sign after typing the decimals, which would be... weird).
And finally, keep in mind that this might not be a cross-browser safe solution. If you need a pure-JavaScript solution, you will need to refine it (i.e., this might not work in IE).
Edit: of course, showing an error message instead of resetting the input field to the previous value is also perfectly possible (updated JSFiddle):
(function() {
var pattern = /^(?=.)\d*(?:[.,]\d+)?$/;
var error = document.getElementById('error');
document.getElementById('myInput').onkeyup = function(event) {
event = event || window.event;
var newValue = event.target.value || '';
if (newValue.match(pattern)) {
error.innerHTML = '';
} else {
error.innerHTML = 'Not a valid number!';
}
};
}());
I leave it up to you to replace the alert with something more user-friendly.
The easiest solution would be something like this
// Returns true on valid, false on invalid
function myInputFilter(input)
{
var value = input.value;
var regex = /^[\d\,\.]*$/;
if(!regex.test(value))
return false;
return true;
}
You could edit the function to just take a string argument, but I've chosen to have it accept the text input element instead. The RegEx can be replaced by anything, I've made a simple one for this example. I would refine it a bit if I were you (You can use the excellent online tool RegExr)
Here is an example of the filter implemented
http://jsfiddle.net/kVV77/
You can use following regular expression:
/^[+-]?(?=.)(?:\d+,)*\d*(?:\.\d+)?$/
to allow only any number of comma and only one dot . with the condition that number cannot start with a comma. Number can have optional + or - at the start.