Reputation: 1155
I need to allow user to enter English and French characters (so I need to take in consideration chars like é,à,ç,è...etc). I also need to allow user to enter symbols and numbers.
In other words I need to prevent the user from entering non latin characters like arabic, chinese, japanese, russian ...etc
$("input:text,textarea").keydown(function(event) {
var allowed = /[A-Za-z0-9àâçéèêëîïôûùüÿñæœ!@#\$%\^&\*\(\)_|}{~">:<\?/\]\[\\=\- ; ]/g;
var key = String.fromCharCode(event.which);
if (event.keyCode == 8 || event.keyCode == 37 || event.keyCode == 39 || allowed.test(key)) {
return true;
} else {
return event.preventDefault()
}
});
This works well except for some characters like ; ? / Ù even though I aded them
Upvotes: 2
Views: 2439
Reputation: 38
I've seen that you're currently using jquery and the answer given was using pure Js, so here's a solution using Jquery:
$("input:text,textarea").on("input",function(event) {
const notAllowed = /[^a-zàâçéèêëîïôûùüÿñæœ0-9 \\?!@#$%^&*":;~=|\-(){}<>[\]]/i;
return (function(event){
const val = event.target.value;
if(notAllowed.test(val)){
//Show error message
// Your code here ...
// Getting old value
let old = $(this).data('val')? $(this).data('val'):'';
event.target.value=old
}
else{
//Remove error message
// Your code here ...
//Setting current value in order to use in case a non permitted character is entered
$(this).data('val',event.target.value);
}
})(event)
});
you can then implement an error message
Upvotes: 1
Reputation: 106483
There's a couple of issues with the pattern you've showed. For a start, you don't have to escape all the metacharacters inside []
definition, but only those that might lead to its incorrect parsing. So quite a bit of those \
are redundant.
Also, be aware that patterns designed just for .test
purposes should in general avoid /g
flag, as this leads to some surprising results:
const patt = /[ab]/g;
console.log( patt.test('a') ); // true
console.log( patt.test('b') ); // false
Finally, as you didn't make your pattern case-insensitive, all the capital letters with diacritics will be missed by that. You can fix this by adding /i
flag, which should work in your case, though, but... that's not the true reason your code fails.
See, the far bigger problem than all the previous combined is using event.which
in combination with keydown
event. Again, there are some minor issues with the approach overall (for example, how can you prevent user from pasting incorrect values inside the form - with or without a mouse?), but the particular thing that bites you is inconsistency of event.which
results:
The which read-only property of the KeyboardEvent interface returns the numeric keyCode of the key pressed, or the character code (charCode) for an alphanumeric key pressed.
Simply said, String.fromCharCode() line works fine only for Latin characters and digits keys on the keyboard - but not for all the rest of the symbols you want to allow in the input:
// `0` pressed:
event.which - 48
String.fromCharCode - '0'
// '-' (neighbor of 0) pressed:
event.which - 189
String.fromCharCode - '½'
Now I suppose the real question is 'well, how to avoid this?'. One possible approach is checking event key property instead, which is now universally supported:
The KeyboardEvent interface's key read-only property returns the value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout.
In this case, however, you still need to handle copy-paste too, as well as some edge cases on keyboard input itself.
What I'd suggest as alternative is using input
event to test the whole value of the input after the change, and not just individual letters. While input
event is not cancellable, it's possible to revert the change it introduces, like this:
document.querySelector('input').oninput = (function() {
let oldValue = '';
const smallEl = document.querySelector('small');
const notAllowed = /[^a-zàâçéèêëîïôûùüÿñæœ0-9 \\?!@#$%^&*":;~=|\-(){}<>[\]]/i;
return function(event) {
const val = event.target.value;
if (notAllowed.test(val)) {
event.target.value = oldValue;
smallEl.removeAttribute('hidden');
}
else {
oldValue = event.target.value;
smallEl.setAttribute('hidden', '');
}
}
})();
small {
color: red;
}
[hidden] {
display: none !important;
}
<label>Let's put some text here: <input /><br />
<small hidden>Non-Latin alpha characters are not allowed</small></label>
This works, but has two caveats. First, the validator now has to store the previous value of input (which raises its complexity a bit). Second, when incorrect chars are thrown into a mix and the value of input is rewritten, cursor always jumps into the end of the line. Not great, but definitely not terrible to me.
All of this won't be necessary when beforeinput
event will be properly implemented in all the browsers. At this moment (Nov 2020) Firefox has it hidden under experimental
flag (here's the ticket), and Chrome seems to not handle its cancellation correctly:
document.querySelector('input').addEventListener('beforeinput', (function() {
const smallEl = document.querySelector('small');
const notAllowed = /[^a-zàâçéèêëîïôûùüÿñæœ0-9 \\?!@#$%^&*":;~=|\-(){}<>[\]]/i;
return function(event) {
const val = event.target.value;
if (notAllowed.test(val)) {
event.preventDefault();
smallEl.removeAttribute('hidden');
}
else {
smallEl.setAttribute('hidden', '');
}
}
})());
small {
color: red;
}
[hidden] {
display: none !important;
}
<label>Let's put some text here: <input /><br />
<small hidden>Non-Latin alpha characters are not allowed</small></label>
When I test it locally in Chrome 87, the input gets stuck immediately after processing the first 'incorrect' character, disallowing any further action on that input. There are other issues with beforeinput in Chrome, some of them stay opened for ~2 years already, so I wouldn't place my bets on this event's usability in the nearest future.
As a final sidenote, one should strongly consider checking this UX Experience thread to validate the whole approach from UX perspective. While Prevention is the only way in some cases, in others it might just lead to user frustration ("why doesn't this stupid app let me put my proper name in it???" is the typical reaction), so choose your options wisely.
Upvotes: 2
Reputation: 328
Filtering keypresses can work to eliminate the characters you don't want to allow, although you'll also need to handle copy/paste too.
Filter on Unicode character ranges is the most concise method I think. Pick the ranges / chars you want to allow from here: https://en.wikipedia.org/wiki/Latin_script_in_Unicode
// Allow backspace, left and right arrows
if ([8, 37, 39].includes(event.keyCode)) {
return true;
}
// Check for disallowed characters
const re = /[^\u0020-\u00ff\u0152\u0153]/;
var key = String.fromCharCode(event.which);
if (!re.test(stringValue)) {
return true
}
Upvotes: 0