Prevent unwanted characters in <input>

Michal Ševčík
4 min readNov 7, 2018

--

I needed to create an <input> that would accept only numbers and a slash symbol (/) - because pattern for birth number in Czech republic is xxxxxx/xxxx where all x are numbers. You can probably come up with many scenarios like this - e.g. in my country the commonly used date format is dd.mm.yyyy so an <input> that accepts only numbers and dots would be nice.

The pattern attribute

The pattern attribute on <input> element is handy, but it allows you to type in just anything and gives you warning only on form submission.

Form with input[pattern] example
Warning on form submission

Input events

There are many <input> events you can listen to on an <input> element, but suitable ones for this scenario are keydown, keypress and beforeinput.

1. keydown

The keydown event is fired when a key is pressed down. The event.key gives you information about what character was typed in, or in other words, what is going to appear in the <input>. So for character 2 you get event.key equal to 2 , for character á you get event.key equal to á.

So you can use this event.key to check whether or not you want the character appear in the <input> box.

Keydown — prevent default behaviour when event.key does not match regexp

That’s nice and all, but note that keydown event is fired for all keys on your keyboard, meaning if you press Shift key, event.key will equal to Shift. Or Backspace. Or Meta. Or Control. Or Enter. Or …

Say you want to delete some characters from the <input>. You press Backspace and get event.key equal to Backspace. And because string "Backspace" contains letters, event.preventDefault() gets called. And you’re unable to delete characters.

2. keypress

Better alternative is keypress event. The keypress event is fired when a key that produces a character value is pressed down.

That sounds much better!

Keypress — prevent default behaviour when event.key does not match regexp

So the event is now fired only on key press that produces a character leaving Backspace (and Enter, and Shift, and ..) out of the game. Now you can validate the pressed key and also you’re able to delete characters.

Note that keypress event should be replaced with beforeinput event (https://www.w3.org/TR/DOM-Level-3-Events/#event-type-keypress).

Keypress event deprecation

Gotchas

Oh yes, there’s always a gotcha.

The keypress event should fire when a keypress produces a character value.

If supported by a user agent, this event MUST be dispatched when a key is pressed down, if and only if that key normally produces a character value.

Say you want to insert ö letter. This letter is actually composed from 2 characters - ¨ and o.

So what I would expect to happen is:

  1. press ¨
  2. press o
  3. see keypress event fire

But what actually happens is:

  1. press ¨
  2. press o
  3. keypress event does not fire

IMHO this sequence conforms to the specification, last pressed key was o and that key normally produces a character value. Only it should produce the o modified with an umlaut.

And since keypress event does not fire for special characters like ö, it actually inserts such character into the <input> because it does not reach our regular expression.

I’m from Czech republic and we have special characters like ěščřžýáíé (group 1) and some others like ďťň (group 2). The difference between them is that when you use czech keyboard layout, group 1 characters are on the keyboard directly, whereas group 2 has to be created by pressing ˇ and then d or t or n.

So neither keypress event can help us cover all the cases.

Playground

You can test it in this little playground, just press ¨ and then o.

3. beforeinput

I have mentioned beforeinput. Had this event been implemented correctly in browsers it would actually solve my problem.

keydown and keypress events receive an instance of KeyboardEvent as a parameter and beforeinput receives an instance of InputEvent.

Beforeinput — prevent default behaviour when event.key does not match regexp

The difference here is that beforeinput fires on every every character typed in the <input>. So after pressing ˇ and then d you receive 2 events. The character typed is stored in event.data.

After some testing (11/06/2018) when you call preventDefault() on the InputEvent it does not actually prevent putting the character into the <input> (both Chrome and Firefox) even though it should.

W3C documentation reads the InputEvent should be cancellable:

W3C beforeinput documentation

But in Chrome and Firefox you get cancellable set to false.

Chrome and Firefox have incorrect InputEvent implementations

Have a play with it here

Conclusion

At the end of the day I have used the solution with keypress event because so far it seems to be the most usable and reliable one. Though to avoid sending form with invalid characters in <input> I also have the pattern attribute that is being consulted at <form> submission and using a validation on the whole <input>‘s value.

But still I would appreciate either working beforeinput event in browsers or consistent behaviour for different special characters in keypress event.

--

--

Michal Ševčík
Michal Ševčík

Written by Michal Ševčík

Founder of RemoteYeah.com. Remote working enthusiast. Full-stack developer.

Responses (1)