Reputation: 1019
I am trying to build a custom text input for phone numbers that accepts only numbers and the plus (+) symbol; all other characters need to be discarded and not shown on the field.
I am trying to do this using an event handler (onkeydown/onkeypress) and discarding inputs corresponding to other keys. However, I can't figure out a cross-browser way to do it. Here are the approaches that I have tried and don't work:
Using the onkeypress event and looking at event.key to figure out which key was pressed: doesn't work on Chrome (see http://caniuse.com/keyboardevent-key). Is there any cross-browser workaround?
Using the onkeycode event and looking at event.keyCode: does not work when we need to press multiple keys to print a character (for instance, an English keyboard layout requires pressing Shift and = to give +). Furthermore, it allows characters such as !@#$%ˆ&*() to appear, as these appear when pressing Shift and a number. (This is the approach followed in JavaScript keycode allow number and plus symbol only, but it does not help me much ;))
Using the HTML pattern attribute: this does not really seem to prevent people from writing whatever they feel like.
Thanks!
Upvotes: 19
Views: 29090
Reputation: 3475
Always try to make server-side validation, this is meant to 'help' the user, not to validate the data, (You can always open up your console and change the value of the input value however you want)
Now, the method I would use is:
Check the value of the whole input on change and make that value go trough a regex to clean unwanted chars, while keeping track of where the "text cursor" was
const tel = document.getElementById('tel');
tel.addEventListener('input', function() {
let start = this.selectionStart;
let end = this.selectionEnd;
const current = this.value
const corrected = current.replace(/([^+0-9]+)/gi, '');
this.value = corrected;
if (corrected.length < current.length) --end;
this.setSelectionRange(start, end);
});
<input type="tel" id="tel">
You could make a much more 'accurate' regex according to what you want, here is a nice tool for that: RegExr
Upvotes: 9
Reputation: 12864
There is another solution, you can use Array.prototype.filter()
to remove the bad characters, and Array.prototype.join()
to recreate the string before insert it into the input.
You can use oninput
event. It execute a JavaScript when a user writes something in an <input>
field.
See example below
var inputEl = document.getElementById('tel');
var goodKey = '0123456789+ ';
var checkInputTel = function(e) {
var key = (typeof e.which == "number") ? e.which : e.keyCode;
var start = this.selectionStart,
end = this.selectionEnd;
var filtered = this.value.split('').filter(filterInput);
this.value = filtered.join("");
/* Prevents moving the pointer for a bad character */
var move = (filterInput(String.fromCharCode(key)) || (key == 0 || key == 8)) ? 0 : 1;
this.setSelectionRange(start - move, end - move);
}
var filterInput = function(val) {
return (goodKey.indexOf(val) > -1);
}
inputEl.addEventListener('input', checkInputTel);
<input type='tel' id='tel' />
Note : I use input type tel to show default number pad in a smartphone or a tablet.
tel: html5 A control for entering a telephone number; line-breaks are automatically removed from the input value, but no other syntax is enforced. You can use attributes such as pattern and maxlength to restrict values entered in the control. The :valid and :invalid CSS pseudo-classes are applied as appropriate.
Reference : MDN <input>
Upvotes: 17
Reputation: 20238
This robust generic approach is inspired by @R3tep's answer. It allows to define regexp patterns via the data-filter
attribute similar to the pattern attribute:
// Apply filter to all inputs with data-filter:
let inputs = document.querySelectorAll('input[data-filter]');
for (let input of inputs) {
let state = {
value: input.value,
start: input.selectionStart,
end: input.selectionEnd,
pattern: RegExp('^' + input.dataset.filter + '$')
};
input.addEventListener('input', event => {
if (state.pattern.test(input.value)) {
state.value = input.value;
} else {
input.value = state.value;
input.setSelectionRange(state.start, state.end);
}
});
input.addEventListener('keydown', event => {
state.start = input.selectionStart;
state.end = input.selectionEnd;
});
}
<input type='tel' data-filter='[0-9|+]*' placeholder='123+456'>
<input type='tel' data-filter='(\+|(\+[1-9])?[0-9]*)' placeholder='+10123'>
<input type='text' data-filter='([A-Z]?|[A-Z][a-z]*)' placeholder='Abcdefg'>
<input type='text' data-filter='([A-Z]{0,3}|[A-Z]{3}[0-9]*)' placeholder='ABC123'>
Upvotes: 10
Reputation: 2799
Using a combination of a keyup
event listener, some RegEx/String comparison methods, and an intermediate variable, I was able to come up with the following:
var ele = document.getElementById('phone');
var curr = "";
var regexPatt = /^(0|[1-9][0-9]*)$/;
ele.addEventListener('keyup',function(e){
var code = e.keyCode || e.which;
if(this.value.match(regexPatt) || this.value.indexOf('+') > -1 || code == 8){
curr = this.value;
this.value = curr;
} else {
this.value = curr;
}
});
Take a look at the fiddle: https://jsfiddle.net/Lg31pomp/2/
This seems to work with digits, the +
character as well as if the user backspaces the input element.
Upvotes: 2
Reputation: 486
Your input should look something like this:
<input type="text" onkeypress='return isNumberKey(event);'>
and this is the script:
function isNumberKey(evt) {
var charCode = (evt.which) ? evt.which : event.keyCode;
console.log(charCode);
if (charCode != 43 && charCode > 31
&& (charCode < 48 || charCode > 57))
return false;
return true;
}
This is one of the many possible solutions.
Upvotes: -1
Reputation: 103
use input
It Shows default number pad in mobiles.
Hope this will help :)
Upvotes: 6
Reputation: 1913
I suggest you look at this project. https://github.com/igorescobar/jQuery-Mask-Plugin/
You could use it as is:
$('.phone').mask('0000-0000');
Example: https://igorescobar.github.io/jQuery-Mask-Plugin/
Or you could read the source code and check out how they solve it.
Upvotes: 8