I recently learned that jQuery UI was to get its own input mask plugin.
jQuery UI’s upcoming masks
If you’re not sure what an input mask is, it’s where a defined set of rules govern what text can be input into a field. As you type, the text in the input mask may be altered to a specific format, like a telephone number or date, or text may be blocked completely, when entering a number where the rules specify a letter should be, for instance.
Thankfully, we don’t see much input masking on the web. Most web authors stick to validation and proper labelling to ensure their visitors enter values correctly. I suspect this is due to the difficulty involved in making masked inputs work well in a cross browser manner. The jQuery UI team must at least see a demand for input masks to be considering implementing them, but I’ve yet to see one in action on a large production website.
I chose the word ‘thankfully’ because I’ve never been a big fan of masked inputs. Aside from the cross-browser issues, you also have to deal with accessibility issues. Here’s a short list of some of the problems a developer will run into when creating a masked input:
- Ranges are awkward to work with, and you’ll struggle to do this right without using them.
- Messing around with the value of an input whilst the user is typing can (and will, in most cases) break the browser’s undo function.
- If you’re masking the input with text, it’s hard to get it to look good, especially with a variable-width font.
- Complex masks can be confusing for the user. For example, the British post code has several variations, messing around with the formatting whilst the user is typing could confuse them because what they typed doesn’t look like their post code.
Some examples I’ve seen try and mitigate these problems by using a different approach, like using multiple inputs and moving the focus from one to the next as the user is typing. These come with their own host of accessibility problems, like preventing copy/paste on the whole text.
My short journey to the dark side
I decided that, rather than just complain about current implementations of masked inputs, I’d see if I could come up with anything better. So, while the SO was watching the telly, I set about putting an idea into practice. And it was tough! You can see the fruits of my labour here:
[iframe http://jsfiddle.net/AndyE/hnJhY/show/light 100% 60px]
I know, it’s very basic. It looks and works best in IE, though that’s purely accidental. I ran into several annoying cross browser issues along the way and didn’t get much time to polish it up. As a proof-of-concept, though, I think it gets the point across. It works by overlaying a
<span> element above the text field, and that element contains the mask text with each letter of text being wrapped in its own span element. This allowed me to vary the colour of the static
pointer-events: none is used so that WebKit and Gecko allow you to click through the span to focus the input text. In IE, I cleverly — if I may say so myself — set
contentEditable = 'true' on the
<span> so that, when it is focussed, the caret position is obtained and seamlessly passed to the
<input> at the correct position.
Despite its shortcomings, I’m rather pleased with the result. It allows undo to continue working in WebKit/Trident, and this functionality could be extended to Gecko, at least. However, there are several serious issues with the approach, the biggest being that text positioning in replaced elements is not the same as in non-replaced elements. In IE, the mask text and the input text are perfectly aligned. In most other browsers, the input text is a pixel or so off. Someone with more time on their hands may be able to come up with a way to feature detect this and adjust the input padding values accordingly.
If you’re thinking about using a masked input, I’d advise you to really weigh up whether it will really give you more than good validation and proper labelling your input fields. If you need guidelines for what your users should enter, the HTML5
placeholder attribute will serve you well enough, and there are plenty of shims to get the attribute working in unsupported browsers. If you decide to use a masked input anyway, then at least use a nice looking fixed width font.