How to create input masks in HTML

Download HTML Input Mask

Note that this is not compatible with all browsers, has known problems and limitations, and I am not maintaining it or replying to requests for help. Thanks! (But also note that you are free to change and redistribute under the license terms, which you should read after downloading)

Have you ever wanted to apply an input mask to an HTML form field? Input masks are common in traditional GUI applications, but HTML has no such feature. This article introduces a library that adds input masks to form fields with unobtrusive JavaScript.

What’s an input mask?

View the Demo

Input masks are guides to help users enter data in the correct format. They typically do not actually validate data; they just ensure the right types of characters are entered in the right places. Typical uses are for dates, times, social security numbers, phone numbers, and credit card numbers. The user enters un-formatted input, and the mask takes care of adding dashes and other separators in appropriate places.

For example, in the United States most people use MM/DD/YY format to write dates. A well-written GUI application honors the user’s locale and creates an appropriate input mask, such as ##/##/##, for date entry. The user types the numbers, and the program inserts the slashes. If the user types something other than a number, that character is discarded, not entered into the field.

How to do this with JavaScript

There are several problems you need to solve to simulate this in a web browser. First things first: let’s state the requirements.

  1. Help the user avoid entering invalid characters.
  2. Automatically insert separators as the user types.
  3. Constrain the length of the input.

Second, let’s create a spec for the masking syntax. In Windows Forms programming, controls have a Mask property, and other GUI libraries have similar functionality. The full behavior of these masks is complex. For an example, see the MSDN documentation for masked edit controls. You can get a lot of that functionality with a simpler specification, though. The following will suffice for many uses:

  1. The mask only allows one type of character for the entire mask. For example, the mask can allow either all digits or all alphanumerics, but you can’t constrain one character to be a digit while letting other characters accept alphanumerics.
  2. The mask specifies the placeholders for input with spaces, and separators as non-spaces.

An example mask, then, has two parts: the format, which says which places can accept user input, and the type, which says what type of character can go in those places. We’ll see how to actually do this later.

The third problem is to unobtrusively attach the masking functionality to input fields, with gracefully degrading behavior if the browser doesn’t support it, and without adding a lot of markup to your forms to specify the mask format and type. This is easy, using the principles I laid out in an earlier article on using classes to specify data types. This technique is 100% appropriate because classes aren’t just hooks for CSS, they’re general-purpose processing information. This lets you easily specify a) which inputs get masks, and b) which type of mask they get.

How it works

To add masks to form fields, reference my library, then make the page’s load event fire the Xaprb.InputMask.setupElementMasks() function in my library. This will find all elements with the class input_mask, which specifies that the element should get a mask. Each element should also have a mask_??? class, where the ??? specifies which mask to attach. The library takes care of the rest.

By the way, this library depends on the Prototype library, so you will also need to reference that in your page. If you don’t, you won’t get an error, but nothing will happen.

The setup function iterates over the elements and connects a callback to the onkeypress event. The callback is created by another function. To decide which mask to apply, it does a regular expression match against the element’s className. If the element’s class is “input_mask mask_date_us“, the regular expression captures “date_us,” and looks up the date_us mask. Here’s how that is defined:

      date_us: {
         format: '  /  /    ',
         regex:  /\d/,
      }

The format property is a string with spaces where input should go, and other characters get inserted automatically. The regex property is a regular expression that matches a valid character, in this case a digit.

Here’s how the callback function works: when it fires, it checks each character in the form field’s value. If there’s a space in that place in the mask’s format string, it looks to see if the character matches the mask’s regular expression. If so, the character is valid for that place in the input; if not, the character is rejected. If there isn’t a space in that place in the format string, the character from the format string is copied into the form field (this is how separators are automatically inserted).

Demonstration

Enough talk, let’s see it in action. This demonstration of Javascript form input masks shows a few of the masks I discussed above: US date, time, and phone number.

If you like the way the form input fields look, you can thank the fine folks at Particletree. I borrowed the styling from their article on how to make forms suck less (it makes the borders of the input areas easier to see).

Limitations

Since this is really just a hack on top of existing HTML form inputs, there are some things that will never work quite as well as a natively designed widget (the same is true for my JavaScript Combo Box widget). Here are some of the limitations:

  • No unicode or international characters (this might be easy to fix).
  • No spaces as placeholders. Sometimes you might want spaces between user input, rather than non-space separators.
  • Only one type of character for the entire input; you can’t constrain the first character to be a digit, and the second a letter.
  • It doesn’t show the mask ahead of time and let the user ‘fill in’ the missing characters; instead, it reveals the mask as the user types.
  • You can’t have two adjacent separators.
  • You can’t type into the middle of the text; all input you type is appended to the end.
  • It hijacks things like Ctrl+A to select all.

Despite the length of that list, these are such minor things (except for maybe international characters) that it’s practically a complete implementation. And as far as I know, everything here could be solved easily. I just haven’t done it, because you haven’t yet told me which things are problems for you (hint, hint: leave a comment, and patches are very welcome). I deliberately kept things really simple in this first version. Future versions can get fancier, or not.

Conclusion

So that’s it! Simple, lightweight, intuitive input masks. With a proper form validation library on the back-end, you should be able to use this to help your users enter data in the format you desire. Again, let me know what you think, and by all means improve this, and send me the results!

Technorati Tags:No Tags

You might also like:

  1. JavaScript date chooser
  2. Javascript date parsing and formatting, Part 2
  3. JavaScript number-formatting library updated
  4. JavaScript date parsing and formatting, Part 1
  5. How to find and fix invalid character data in MySQL

29 Responses to “How to create input masks in HTML”


  1. 1 sn

    Awesome. Thanks for this!

  2. 2 Neil Crosby

    This looks pretty groovy as a starting point for HTML input masks. One thing that put me off though was that whilst you might actually type, say, “12/07/2006″ into the date field the forward slash characters would not actually appear until you typed the first numeral of the next section of date.

    As a user this was slightly confusing as I would expect that character to appear as I typed it rather than being forcibly ignored and then manually added once I typed the next numeric character.

    I’d also absolutely love to be able to see some sort of grayed out instance of the input mask on the input field as I’m entering data into it, but this is a wish rather than a worry.

    Definitely very cool as a proof of concept though!

  3. 3 Nate K

    Very cool concept! Nice work.

  4. 4 Xaprb

    Neil, I was thinking the same thing about the grayed-out representation, but I don’t know how to do that except with a background image, which might not match the user’s font :-/

    I was also thinking of how to let the user type the separator as an indication that they’re ready for the next ‘field’ in the input string. This would involve a default character for any un-filled spaces. So, for example in the date field, I might want to type 5/5/2006. Every time I type / the script would look ahead and see that I just typed the ‘next’ separator character. It would then left or right-pad the current field’s value with the default, add the separator, and I’d be in the next field. To clarify: I type 5, then /. The script left-pads the 5 with 0 and adds /. I type 5/ again, and again the script converts that to 05/. The end result is 05/05/2006.

    Another improvement might be to anticipate the next separator, so after (in your example) you type 12, it would add the slash, not waiting for you to type the 0.

    Maybe I should make a proof of concept and see if it’s usable, and whether other people like it.

  5. 5 Gavin Brown

    Erm I’m confused, I’m not really sure what this is trying to acheive or maybe the example is broken???

    e.g. I can enter “:8/90/0654″ for a date and “99:`2′#1″ for a time ? (Note: This was in IE 7).

  6. 6 Xaprb

    No, you’re right, it doesn’t cancel input on special characters in IE. I’ve been learning more about this over the last few days, but basically it looks like every browser reports the keyboard event’s keycode differently, in really bizarre ways. For example, IE reports the colon as a non-printable character. I have not yet found a workaround for this. It looks like one of those pain-in-the-butt browser things.

  7. 7 Sava

    At first I thought you we’re disabling the inputs. But now I see what you really meant. That is really cool …

    Does it work only with numbers or you can use them for anything ?

    Sava

  8. 8 Xaprb

    It should work with anything you can regex-match, but unfortunately as I mentioned in the above comment, IE is giving me trouble. I haven’t found a workaround yet. I don’t really want to write such specific code. I suppose one thing I could do is write another module to examine an event and return the character in a cross-browser manner, but I haven’t had time to do that yet.

  9. 9 John Reeher

    In IE7 the numeric keypad doesn’t work to enter numbers. This is because the regexp doens’t match with the keypad characters. To correct this I added the following code to the applyMask function:

                // correction for numeric keypad
                if (key >= 96 && key <=105)
                {
                  key = key - 48;
                }
                var ch      = String.fromCharCode(key);
  10. 10 kyle

    I have been playing with this for a few days on some of my pages. One page has multiple forms in an absolute position. They are swapped using $(id).style.visibility. The init for the input masks won’t work if they are hidden. I was trying to call it when the view was swapped, but if you go to one view then back to the other, it is called twice. If you start to enter data into a field, it will duplicate whatever you type. If you type “a” it will put “aa”, “1″ becomes “11″, etc. Is there a way to kill and reinit on each view call?

  11. 11 Xaprb

    It sounds like two elements in your HTML document have the same ID. This is invalid and will cause all sorts of problems. IDs are supposed to be globally unique. You should consider running your document through a validator.

  12. 12 Aaron Petersen

    I had a question about the delete key. With the mask it appears to handle for the backspace key great, but for some reason when I highlight the mask and hit the delete key, it doesn’t erase the date that I had entered into the mask.

  13. 13 Xaprb

    That’s because the code cancels the event unless the key pressed is a printable character. Not the best way to do it, I know.

    I have tried to completely solve this and other problems, but capturing events and trying to figure out what the key pressed was is a thorny problem, with ridiculous browser incompatibilities. I have come to the conclusion that this is not a good way to simulate an input mask. However, I don’t know anything else that would work at the moment. It’s uglier than I thought when I wrote this article.

  14. 14 ferg

    Q - I am using this to limit number inputted but it seems to prevent users in IE6+ from inputting using the keypad on the right. ie they can only input numbers fro the top of the keyboard. Is there anyway around that?

  15. 15 Xaprb

    Not that I’m aware of.

  16. 16 Mindaugas

    On ie7 prototype library v 1.4 caused an error.
    I downloaded v1.5, but mask doesn’t work any more.

  17. 17 Mindaugas

    emmm sorry, now it again works.. mistery..thank you for this post anyway.
    Though i left prototype version 1.5, last one.

  18. 18 leung

    hey,

    Is it posible to insert only numeric and a sign like ‘-’
    example: 12-542-45-63 or like a price 12,36

    How do i have to write that

    num: {
    format: ' -  ' ,
        regex: /d/   // here i would like to include ' - ' sign
    },
  19. 19 Werner Cloete

    Good day, everyone…

    I changed the input masks’ place holders to enable us to use spaces in our masks…

    Here is a sample of the new masks:

    		date:
    		{
    			format:	'§§/§§/§§§§',	// Werner Cloete - 02/05/2007 - Make use of a new "§" mask place holder
    			regex:	/d/
    		},
    		phone:
    		{
    			format:	'§§§ §§§-§§§§',	// Werner Cloete - 02/05/2007 - Make use of a new "§" mask place holder
    			regex:	/d/
    		}
    

    The “§” character is created with [Alt]+21…

    I also updated the code to display the mask’s next set character as you type…so, for example, if you’re using the date mask, and you have already typed “12″, the “/” will immediately appear…

    Here is the updated code:

    /*					if ( mask.format.charAt(pos - 1) != ' ' ) */				// Werner Cloete - 02/05/2007 - Commented out the code
    					if ( mask.format.charAt(pos) != '§' )						// Werner Cloete - 02/05/2007 - Changed the code to use the new "§" mask place holder
    					{
    /*						str = this.value + mask.format.charAt(pos - 1) + ch; */	// Werner Cloete - 02/05/2007 - Commented out the code
    						str = this.value + ch + mask.format.charAt(pos);		// Werner Cloete - 02/05/2007 - Changed the order in which the strings are concatenated
    					}

    I hope that this will prove useful…

    Yours sincerely

    Werner

  20. 20 Oliver

    You have this licensed under the GPL. I’d like to use this code in on of my web application products. Will you give your permission for this use?

  21. 21 Xaprb

    It’s LGPL. I don’t want to license it any more liberally than that, though of course you are free to inspect the source and design your own implementation of the same thing.

  22. 22 Jay Nair

    thank u so much for this, it really helped me a lot. can the delete key and the numbers on the num pad be activated? if yes, please let me know. thanx once again.

  23. 23 Jimmy6

    it does not work in IE7. “/.;’,” all there symbol can be inserted

  24. 24 Rhett135

    Very nice, helpful!
    Is there a smaller version of prototype.js available, with only the necessary code for this mask?

  25. 25 Muhammad Asif

    Can I place a mask inside the text field pre populated. This will give a clue to the end user entering the date. Thanks. I am unable to do so till now.

  26. 26 Xaprb

    Hi all,

    I’m sorry but I don’t have time or need to maintain this or help people with it. I would love it if someone would take on the task of maintaining it. Please leave a comment with the URL of the new home for it, if you decide to maintain and improve it.

  27. 27 Mike

    To enable the keypad in IE7.0 replace the applyMask function with the code below.


    applyMask: function(event) {
    var match = /mask_(\w )/.exec(this.className);
    if ( match.length == 2 && Xaprb.InputMask.masks[match[1]] ) {
    var mask = Xaprb.InputMask.masks[match[1]];
    var key = Xaprb.InputMask.getKey(event);

    // correction for numeric keypad
    if (key >= 96 && key

  1. 1 zed23 » Blog Archive » Quick updates
  2. 2 Daily misery » Blog Archive » Input mask revisit

Leave a Reply

Please do not use this blog to get help with problems or bugs in Maatkit or innotop: use the Sourceforge forums, mailing list, or bug trackers. If you're asking for help with MySQL, please use the MySQL mailing list instead.