State of the Masked Input Plugin
This post should be titled, "Dude, Where the Hell have You Been?" I'm sorry I have ignored this project for so long.
New Home
First, let me get started by saying that I moved the source to github a while back. It now lives here. If you look at the commit history, I moved it there months ago, fixed a few outstanding bugs, added a feature that someone needed for a specific project and then just left it. I'm picking it back up somewhat, but we'll talk about that in a minute.
Email Bankruptcy
There have been a TON of emails from you guys; so many that I haven't been able to keep up. Most of the emails have been about two bugs: an off by one goof I made for the completed function and forgetting to use charAt() to access a char in a string. These bugs are fixed in the github repo right now (I think). I'm calling my email situation a total loss. Sorry to those that have emailed me and not gotten a response.
Birth of a New Project
Part of the reason I stalled on this project was a lack of tests. For a while I wasn't sure how to even test this thing given that it is purely driven from user input. Up to this point I had just been opening up a test page in every browser I could think of and running through a few things manually. A couple of weeks ago I sat down one night and spiked out a rough version of a keystroke simulator which I'm now calling KeyMasher. Please be kind, it's still very rough. Once I get it more polished, I'll put up an official project page on my blog here. I had found a few other projects which do this and jquery.autotype seemed to be the closest fit. Unfortunately I couldn't get it to work with my specific needs, so I've now written my own with a syntax I feel more comfortable with. I've already worked out a few tests using this against my masked input plugin.
I'm Just One Guy
After I get a half way acceptable set of tests around it, then I can feel a bit more confident about what I change. I would like to be able to implement some of the features I've seen come across my email. It will take some time to get everything to a place where it should have already been. Please be patient, I'll get there. ![]()
Comments(68)
masked input with default ghost text in the fields..how cool is that:
/*
Masked Input plugin for jQuery
Copyright (c) 2007-2009 Josh Bush (digitalbush.com)
Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
Version: 1.2.2 (03/09/2009 22:39:06)
*/
(function($) {
var pasteEventName = ($.browser.msie ? ‘paste’ : ‘input’) + “.mask”;
var iPhone = (window.orientation != undefined);
$.mask = {
//Predefined character definitions
definitions: {
’9′: “[0-9]“,
‘a’: “[A-Za-z]“,
‘*’: “[A-Za-z0-9]”
}
};
$.fn.extend({
//Helper Function for Caret positioning
caret: function(begin, end) {
if (this.length == 0) return;
if (typeof begin == ‘number’) {
end = (typeof end == ‘number’) ? end : begin;
return this.each(function() {
if (this.setSelectionRange) {
this.focus();
this.setSelectionRange(begin, end);
} else if (this.createTextRange) {
var range = this.createTextRange();
range.collapse(true);
range.moveEnd(‘character’, end);
range.moveStart(‘character’, begin);
range.select();
}
});
} else {
if (this[0].setSelectionRange) {
begin = this[0].selectionStart;
end = this[0].selectionEnd;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
begin = 0 – range.duplicate().moveStart(‘character’, -100000);
end = begin + range.text.length;
}
return { begin: begin, end: end };
}
},
unmask: function() { return this.trigger(“unmask”); },
mask: function(mask, settings) {
if (!mask && this.length > 0) {
var input = $(this[0]);
var tests = input.data(“tests”);
return $.map(input.data(“buffer”), function(c, i) {
return tests[i] ? c : null;
}).join(”);
}
settings = $.extend({
watermark:”",
watermarkClass:’ghost’,
placeholder: “_”,
completed: null
}, settings);
if(settings.watermark===”") settings.watermark=mask;
var defs = $.mask.definitions;
var tests = [];
var partialPosition = mask.length;
var firstNonMaskPos = null;
var len = mask.length;
$.each(mask.split(“”), function(i, c) {
if (c == ‘?’) {
len–;
partialPosition = i;
} else if (defs[c]) {
tests.push(new RegExp(defs[c]));
if(firstNonMaskPos==null)
firstNonMaskPos = tests.length – 1;
} else {
tests.push(null);
}
});
return this.each(function() {
var input = $(this);
var buffer = $.map(mask.split(“”), function(c, i) { if (c != ‘?’) return defs[c] ? settings.placeholder : c });
var ignore = false; //Variable for ignoring control keys
if(input.val()===settings.watermark) input.val(“”);
var focusText = input.val();
input.data(“buffer”, buffer).data(“tests”, tests);
function seekNext(pos) {
while (++pos = 0);
for (var i = pos; i < len; i++) {
if (tests[i]) {
buffer[i] = settings.placeholder;
var j = seekNext(i);
if (j < len && tests[i].test(buffer[j])) {
buffer[i] = buffer[j];
} else
break;
}
}
writeBuffer();
input.caret(Math.max(firstNonMaskPos, pos));
};
function shiftR(pos) {
for (var i = pos, c = settings.placeholder; i < len; i++) {
if (tests[i]) {
var j = seekNext(i);
var t = buffer[i];
buffer[i] = c;
if (j < len && tests[j].test(t))
c = t;
else
break;
}
}
};
function keydownEvent(e) {
var pos = $(this).caret();
var k = e.keyCode;
ignore = (k 16 && k 32 && k = 32 && k 186) {//typeable characters
var p = seekNext(pos.begin – 1);
if (p < len) {
var c = String.fromCharCode(k);
if (tests[p].test(c)) {
shiftR(p);
buffer[p] = c;
writeBuffer();
var next = seekNext(p);
$(this).caret(next);
if (settings.completed && next == len)
settings.completed.call(input);
}
}
}
return false;
};
function clearBuffer(start, end) {
for (var i = start; i < end && i < len; i++) {
if (tests[i])
buffer[i] = settings.placeholder;
}
};
function writeBuffer() { return input.val(buffer.join('')).val(); };
function checkVal(allow) {
//try to place characters where they belong
if(input.val()===settings.watermark) input.val("");
var test = input.val();
var lastMatch = -1;
for (var i = 0, pos = 0; i < len; i++) {
if (tests[i]) {
buffer[i] = settings.placeholder;
while (pos++ test.length)
break;
} else if (buffer[i] == test[pos] && i!=partialPosition) {
pos++;
lastMatch = i;
}
}
if (!allow && lastMatch + 1 = partialPosition) {
writeBuffer();
if (!allow) input.val(input.val().substring(0, lastMatch + 1));
}
return (partialPosition ? i : firstNonMaskPos);
};
if (!input.attr(“readonly”))
input
.one(“unmask”, function() {
input
.unbind(“.mask”)
.removeData(“buffer”)
.removeData(“tests”);
})
.bind(“focus.mask”, function() {
input.removeClass(settings.watermarkClass);
if(input.val()==settings.watermark)
input.val(“”);
focusText = input.val();
var pos = checkVal();
writeBuffer();
setTimeout(function() {
if (pos == mask.length)
input.caret(0, pos);
else
input.caret(pos);
}, 0);
})
.bind(“blur.mask”, function() {
checkVal();
if (input.val() != focusText){
input.change();
}
if (input.val()===settings.watermark || input.val()===”")
input.addClass(settings.watermarkClass);
})
.bind(“keydown.mask”, keydownEvent)
.bind(“keypress.mask”, keypressEvent)
.bind(pasteEventName, function() {
setTimeout(function() { input.caret(checkVal(true)); }, 0);
});
checkVal(); //Perform initial check for existing values
});
}
});
})(jQuery);
Josh, do you plan on implementing a currency mask?
Currently when I save the record, the mask is saved within the field.
To avoid this I had to remove the mask manually before save ?
I figured I would mention this on this page as well. I created an issue at github for this project but thought I would bring this to other’s attention.
There is a production defect that may be considered an issue pending on what type of website you are running, but this is how the bug can be reproduced.
“There is a significant defect with this plugin dealing with the copy/paste functionality which can bring the browser to its knees.
To repeat the steps –
1. Copy a value from an external source (notepad) into an input field in the browser that is masked.
2. Directly go from the input field to an external source. The last focus should have come from that masked field.
3. Copy another value from an external source (notepad) into a different field that is masked. (Going from a masked field, external source, directly to another masked field.)
4. The masking gets caught in an infinite loop. The browser can be minimized and then maximized to solve the issue, otherwise the browser must be killed.”
I second the ‘value includes mask’ issue.
I would like several flavors of value retrieval methods… at least ‘with mask’ and ‘without mask’, and maybe with selective mask parts removal.
Example (guess you got the idea, but still…):
’2011/01/19′ : may be retrieved as-is or ’20110119′
Thanks for the awesome plugin!
The infinite loop defect has been fixed.
Thanks for the quick turn around!
Is there a way to mask a date and also verify if it’s a valid one?
Hi,
I have a text box in which I have to show the following format:
[three alpha numeric]/1-5 numbers of digits/[A-Z]
Eg: VAZ/5683/D, kly/9/S
for that I have written the code as follows:
jQuery(function($) {
$(“#consumerNo”).mask(‘aaa/9?9999a’);
}
);
What happens is, if I am entering VAZ/5683_/D and as I go to the next text box ‘D’ gets disappeared. On focus it returns. But when we submit the form that ‘D’ is not passed to the server. Any ideas ?
I made some changes to to the script to trim off additional _ characters
.bind(“blur.mask”, function()
{
checkVal();
input.val(rtrim(input.val(),settings.placeholder));
if (input.val() != focusText)
input.change();
})
//I needed added this function
function rtrim(str, chars)
{
chars = chars || “\\s”;
return str.replace(new RegExp(“[" + chars + "]+$”, “g”), “”);
}
Hi Josh,
I am using your excellent input mask on a date field using the mask of dd/mm/yyyy. I configure it something like:
$.mask.definitions['d']=’[0123]‘;
$.mask.definitions['m']=’[01]‘;
$.mask.definitions['y']=’[12]‘;
$(‘#my-date’).mask(‘d9/m9/y999′,{placeholder:”-”});
It really does work brilliantly and I’m very happy with it, my question is can I configure the placeholder so that it displays ‘dd/mm/yyyy’ instead of ‘–/–/—-’? If so, how?
Thanks
Fixed a bug which was causing the cursor to jump when switching between using the mouse and tabbing between fields.
.bind(“focus.mask”, function ()
{
input.addClass(‘focus’);
focusText = input.val();
var pos = checkVal();
writeBuffer();
setTimeout(function ()
{
if (input.hasClass(‘focus’))
{
if (pos == mask.length)
input.caret(0, pos);
else
input.caret(pos);
}
}, 0);
})
.bind(“blur.mask”, function ()
{
input.removeClass(‘focus’);
checkVal();
input.val(rtrim(input.val(), settings.placeholder));
if (input.val() != focusText)
input.change();
})
Hi,
Thanks very much for this plug-in. It was just what I was looking for.
Question. I may not be understanding something, but, is there a method to indicate that a blank is allowed in the * alphanumeric?
We have a situation where we are asking for international phone numbers. Country codes can be from 1 to 3 digits, so we want to allow the user to partially free-form the entry, but still supply some structure to it, such as the opening and closing parentheses for the area code. Since telephone numbers aren’t standardized internationally, we can’t set the format for that, but we want a way of identifying the country code, area/city code, and phone number.
It would be something like:
+9%% (9%%) 9999?%%%%%%%%%%%%%%%%%%%%
In the above, “%” indicates that a space is allowed in the alphanumeric (or that spaces and symbols are allowed).
This isn’t critical, but I thought I’d ask. If we could use the plugin to do this as well, without having to create 5 separate fields to do it, that would be great.
Any ideas?
Thanks for your time.
(And you should add a donation bucket to your site!)
Hi,
Has anyone tried the plugin on Android 2.2 (Firefox or default webkit browser)? It is very buggy. It runs perfctly on other platforms though.
Does anyone has a solution to this “caret positionning” problem?
Thanks.
Last version is worked only in firefox and opera.
In other browsers when I enter first symbol cursor goes to end of field.
Thank you.
Sorry, It’s worked correctly. It was my mistake.
Helow, i use this plugin several times, but there is a issue.
When i save a value on my system, it works fine, but if i get the edit page most of the masked values just disapears, its not a system problem because in the source code its there, so, the plugin removes the value for no reason
Hi there
This is a great plugin. How do I remove the mask from an input field ie unmask.
Thanks
Lee
Hello! Sorry for my english.
plugin version 1.2.2
Firefox 3.6.13
Bug can be reproduced in demo on “http://digitalbush.com/projects/masked-input-plugin/” page.
Steps:
1. Fill first field with any date;
2. Open window of any application (messanger for example), browser lost focus, but masked fields must be visible (window of messanger smaller browsers window);
3. Directly click on second masked field.
4. Repeat step #2 and click on third field.
The masking gets caught in an infinite loop.
Thanks
I have found a bug!
For example (tested on your demo page with IE8):
1. Click on the first textfield
2. Click outside the active browser window
3. Click on another text field, not the same as clicked on in step 1.
4. Tada! The cursor jumps between these two textfields. You can stop it by clicking outside the window again, then on the textfield clicked in step 1.
What can i do to avoid this behaviour?
Thanks for your nice work.
There is any way to change the plugin so the chars get overwritten in place of inserted?
In most cases, when a date’s field is prefilled with a given date, if the user just want to change the day the rest of the field gets destroyed because of insertions.
In Opera masked fields not changes by “TAB” button
Eugene is correct. The tab doesn’t work in Opera with the latest changes.
Fyi for everyone using 1.2.2. There is a newer version out there… its on his github url that addresses some defects.
Thanks for the great plugin. I also needed the ability to submit the data without the mask. This is how I remove the mask after input. The masking return on focus so this is a feasible solution for input guidance. You could bind something similar to the form’s onsubmit event if you want the mask to remain visible until submitting.
$(‘#phone’).mask(‘(999) 999-9999′).bind(‘blur’,function(e){$(e.target).attr(‘value’,$(e.target).mask())})
Dude, thank you so much for this plugin. It works amazingly. I’m watching your GitHub repo and will try and do what I can to help write tests, etc.
Just wanted to give you a big pat on the back.
Josh
I LOVE this script. Thank you for developing it. My only request would be to assign a mask with an infinite number of entries.
For example: if I want someone to enter only numbers but I don’t know if they will enter two numbers or 50, it would be cool to enter something like:
$(‘#myNumber’).mask(‘9+′)
Nice plug-in..But i require validation for only numbers and only characters with mask.In my project using content management system jahia 6.5 , there is input mask.But it’s not working giving any valu..Plz tell me how to do that and any code changes required for that.
Yeah, cool plug-in, thanks!
Will you ever http://www.jslint.com this excellent plug-in?
Sasha can you please tell me what was your problem since I have the same one (In IE7 and IE8 when I enter first symbol cursor goes to end of field, in Firefox works fine). Does anybody else has this problem?
Hi, thanks for this great plugin.
Just a small question: when leaving the input field, is it possible not to clear it if what the user typed is not valid ?
Thanks in advance.
I had a mask of “a*?**”.
Had to remove this plug-in as pressing enter to submit a form sends underscores as part of the submission (eg. Entering “N1″ results in “N1__” being sent).
Nice idea though.
Quick question,
I want to use this for a date of birth field with the option of not submitting the year, since sometimes the day/month is known but not the year. With mask, 99/99/9999, if the year is left blank, none of the other data “mm/dd” come through on the form input? Any way to get some but not all of the date to be submitted?
I Want to use Date mask in Grid, Can you help me
I am wondering if you can make the alphanumeric field automatically capitalize?
Hi there,
Can I mask an input text for a (sort of) valid email?
Also, how do I mask an input for alphabets only? I mean I know ‘a’ is to be used for that – but how do I know how many alphabets will be entered by the user?
Thanks
Is there anyway to make this masking smoother like the DevExpress one? Take a look (http://demos.devexpress.com/ASPxEditorsDemos/Features/MaskedInput.aspx). I have yet to find a mask editor that works as well as theirs when it comes to number input.
First-off thanks for a great plugin…
Anyone figured out how to do regEx for the mask. I need to validate a time, so while;
$.mask.definitions['~'] = ‘[012]‘;
$.mask.definitions['!'] = ‘[012345]‘;
$(“#localTime”).mask(“~9:!9″, {placeholder:” “});
will get close it still allows 34:34, which of course makes no sense. I’ve tried
$.mask.definitions['~'] = ‘[012]‘;
$.mask.definitions['t'] = ‘(?:0[0-9]|1[0-9]|2[0-4])’;
$.mask.definitions['!'] = ‘[012345]‘;
$(“#localTime”).mask(“t:!9″, {placeholder:” “});
and various versions of my definition for t, but can’t seem to get it…?
Josh,
I have an input with a value already set, but whenever I try the mask, it always replaces it with the placeholder instead of the value 95 that I set. Any ideas on how to get the value I want to appear first?
$(‘#LessonWeight’).mask(“99″, {placeholder:” “});
Here is the code that I was using,
thank you for the help.
$(‘#LessonWeight’).mask(“99″, {placeholder:” “});
“”
$(‘#LessonWeight’).mask(“99″, {placeholder:” “});
Sorry for so many posts, my code was in html format and it wouldn’t post!!! But somebody may be able to know the answer to my question, it would be greatly appreciated!
Hi
this is excelent
i would like do this…
$(“#myid”).mask(“99.999.999″,{exclude:”.”});
when i clicked submit button the value will be
$myid=98569293 without point
Tnx.. nice plugin tnx
Other sample willbe
My phone number:
$(“#myid”).mask(“9-99-99-99?999″,{exclude:”-”});
example: 4-93-16-93
on submit button
$myid = 4931693
is posible do that?
Any way to mask by class name? For instance if I have multiple fields that I want using the same mask, can I assign a class name to the fields and have the mask applied to said class name?
If possible, can you provide example code? I think this would be a great feature if it’s not available. It would really simplify things such as variable length lists.
Cannot get this to work. Mask does not appear. What am I doing wrong?
jQuery(function () {
$(‘#SpousePhone’).mask(‘(999) 999-9999′);
});
site master includes:
<script type="text/javascript" src="”>
<script type="text/javascript" src="”>
How I can put a mask that accepts n-numbers decimal separator and only four decimal positions? for example:
0.124
123456789123456789.2541
etc.
I don’t know how.
Please help.
I’m currently using v1.3. I’m having an issue where the change event is not being fired for elements that have the mask applied. I see in the code it does an input.chage() however if I do something like this in my html:
The TestChange() function is never called.
The above post should have had an onchange=”TestChange();” in it.
hello there,
thanks for this great plugin!,
i need IP mask, a solution for this will be great, regex mask will gosu…
How can I specify unlimited number of characters that need to entered in the mask. For example, I have an Email Address I need to maski it so the user can enter any number of characters before and after @ sign
Hi, but how can I past to input field special symbol? I need to show symbol with html-code – “°”. This is the sнmbol of degree.
So, I need the mask: “999999’999””
Hi, but how can I past to input field special symbol? I need to show symbol of degree – “°”.
So, I need the mask: “999°999’999””
But plugin can’t show symbol of degree.
Hi, you need some help?
Im trying to use your plugin in mobile devices and he gets crazy, so… can i help you?
Tks!
Good script, thanks.
But i wonder if i can put not only fixed amount of letter in input field?
Nice script-
Like many have asked, is there a way to remove the mask on submit?
This has been asked before.
How do we REMOVE THE MASK when we exit the input field?
$(“#element”).unmask() <– does not work.
Great plugin. We have had many request for email address masks. Is this something that could be added?
I am new to web apps. Do web developers not use the same jsp to accept and display data for an object?
I collect data on a jsp, persist it to db, and redisplay on the same jsp until the user chooses to move to the next jsp.
the masked input is great for input, but once a form has been submitted and redisplayed, the field has a value, the mask is gone and can not be reapplied with out destroying the value in the form field.
Am I correct? there is no way to apply the masked input to a field with existing value?
For what it does…masked input is fantastic, works without fail. A great addition to the tool set available to everyone developing apps.
Is there any way to prevent an input field from being cleared when the entered input does not conform to the mask. For example, if I have a mask that is ’999999999′ (9 digits) and the user only enters 8 digits, I would like to have those 8 digits remain in the input box so that they don’t have to re-type the value they just entered.
Phil: the way masks work, you really can’t use them for e-mails — because an e-mail address can contain any number of characters before & after the ‘@’ sign. The RFC for e-mails is quite complicated (the RFC defines what is & isn’t allowed). There are regexes for validating an e-mail; the best is Dominic Sayers’ is_email.php (http://code.google.com/p/isemail); you can also use a regex to validate clients-side w/ JavaScript.
If you’ll visit my website’s contact page & email me, I can send you some sample JQuery that validates for e-mail. And remember, many CMSs also have built-in functions for this.
Cheers,
— CZ
First, thanks for such a great plugin.
One issue – I’ve noticed that if you attach a blur() event to a masked field, that event fires *before* the plugin’s completed() event, where you really want it to be firing after. This presents a problem if you have entered, say, an invalid string which will get blanked out, but only *after* the blur() event fires.
awesome plugin – thank you
I’d also love a solution for date entry that only permitted valid entries – this gets me part of the way but still permits some meaningless dates to be entered:
$.mask.definitions['m']=’[01]‘; //month 1st digit
$.mask.definitions['d']=’[0123]‘; //day 1st digit
$.mask.definitions['y']=’[12]‘; //year 1st digit
$.mask.definitions['h']=’[09]‘; //year 2nd digit
jQuery(‘#date’).mask(”m9/d9/yh99”);
How about variable numbers of digits, for a money text input, for example, we could have something like “$ *9.99″.
What you think?
how to set the date using mask.
Hi, great plugin, but! How remove mask when i do submit ?
Thanks in Advance
Good plugin, but i would need an option to avoid shifting while typing. For a date time mask this is a must: if I type 01/05/2011 and want to change 01 for 02, shifting the whole date time is annoying and makes the mask unusable. Anyway, thanks and congrat for your work
I’m need ‘replace on type’ too, as Canal 24 .NET says.
@jerjoham @ March 8th, 2011
Thanks for the great plugin. I also needed the ability to submit the data without the mask so I tried your code.
$(‘#phone’).mask(‘(999) 999-9999′).bind(‘blur’,function(e){$(e.target).attr(‘value’,$(e.target).mask())})
I have found that your code doesn’t work in Google Chrome.
Re: issues with Android platforms. I understand that there may be a bug that occurs in the following scenario:
If you have a maxlength on the input field that is equal to or less than the length of the input mask, you won’t be able to enter data into that field on an Android.
The solution is to either remove the maxlength or increase it to the length of the mask plus 1.
Hope this helps!