@model HelloWorldMvcApp.TestModel
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>JQuery validation and masking</title>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
.field-validation-error {
@Html.LabelFor(model => model.FirstName)
@Html.TextBoxFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
@Html.LabelFor(model => model.LastName)
@Html.TextBoxFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
@Html.LabelFor(model => model.PhoneNumber)
@Html.TextBoxFor(model => model.PhoneNumber)
@Html.ValidationMessageFor(model => model.PhoneNumber)
@Html.LabelFor(model => model.ZipCode)
@Html.TextBoxFor(model => model.ZipCode)
@Html.ValidationMessageFor(model => model.ZipCode)
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/4.0/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript">
Masked Input plugin for jQuery
Copyright (c) 2007-2013 Josh Bush (digitalbush.com)
Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
function getPasteEvent() {
var el = document.createElement('input'),
el.setAttribute(name, '');
return (typeof el[name] === 'function')?'paste':'input';
var pasteEventName = getPasteEvent() + ".mask",
ua = navigator.userAgent,
iPhone = /iphone/i.test(ua),
android=/android/i.test(ua),
//Predefined character definitions
//Helper Function for Caret positioning
caret: function(begin, end) {
if (this.length === 0 || this.is(":hidden")) {
if (typeof begin == 'number') {
end = (typeof end === 'number') ? end : begin;
return this.each(function() {
if (this.setSelectionRange) {
this.setSelectionRange(begin, end);
} else if (this.createTextRange) {
range = this.createTextRange();
range.moveEnd('character', end);
range.moveStart('character', begin);
if (this[0].setSelectionRange) {
begin = this[0].selectionStart;
end = this[0].selectionEnd;
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
begin = 0 - range.duplicate().moveStart('character', -100000);
end = begin + range.text.length;
return { begin: begin, end: end };
return this.trigger("unmask");
mask: function(mask, settings) {
if (!mask && this.length > 0) {
return input.data($.mask.dataName)();
placeholder: $.mask.placeholder, // Load default placeholder
defs = $.mask.definitions;
partialPosition = len = mask.length;
$.each(mask.split(""), function(i, c) {
tests.push(new RegExp(defs[c]));
if (firstNonMaskPos === null) {
firstNonMaskPos = tests.length - 1;
return this.trigger("unmask").each(function() {
return defs[c] ? settings.placeholder : c;
while (++pos < len && !tests[pos]);
while (--pos >= 0 && !tests[pos]);
function shiftL(begin,end) {
for (i = begin, j = seekNext(end); i < len; i++) {
if (j < len && tests[i].test(buffer[j])) {
buffer[j] = settings.placeholder;
input.caret(Math.max(firstNonMaskPos, begin));
for (i = pos, c = settings.placeholder; i < len; i++) {
if (j < len && tests[j].test(t)) {
function keydownEvent(e) {
//backspace, delete, and escape get special treatment
if (k === 8 || k === 46 || (iPhone && k === 127)) {
begin=k!==46?seekPrev(begin):(end=seekNext(begin-1));
end=k===46?seekNext(end):end;
} else if (k == 27) {//escape
input.caret(0, checkVal());
function keypressEvent(e) {
if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore
if (pos.end - pos.begin !== 0){
clearBuffer(pos.begin, pos.end);
shiftL(pos.begin, pos.end-1);
p = seekNext(pos.begin - 1);
c = String.fromCharCode(k);
setTimeout($.proxy($.fn.caret,input,next),0);
if (settings.completed && next >= len) {
settings.completed.call(input);
function clearBuffer(start, end) {
for (i = start; i < end && i < len; i++) {
buffer[i] = settings.placeholder;
function writeBuffer() { input.val(buffer.join('')); }
function checkVal(allow) {
//try to place characters where they belong
for (i = 0, pos = 0; i < len; i++) {
buffer[i] = settings.placeholder;
while (pos++ < test.length) {
c = test.charAt(pos - 1);
} else if (buffer[i] === test.charAt(pos) && i !== partialPosition) {
} else if (lastMatch + 1 < partialPosition) {
input.val(input.val().substring(0, lastMatch + 1));
return (partialPosition ? i : firstNonMaskPos);
input.data($.mask.dataName,function(){
return $.map(buffer, function(c, i) {
return tests[i]&&c!=settings.placeholder ? c : null;
if (!input.attr("readonly"))
.one("unmask", function() {
.removeData($.mask.dataName);
.bind("focus.mask", function() {
clearTimeout(caretTimeoutId);
caretTimeoutId = setTimeout(function(){
if (pos == mask.length) {
.bind("blur.mask", function() {
if (input.val() != focusText)
.bind("keydown.mask", keydownEvent)
.bind("keypress.mask", keypressEvent)
.bind(pasteEventName, function() {
if (settings.completed && pos == input.val().length)
settings.completed.call(input);
checkVal(); //Perform initial check for existing values
<script type="text/javascript">
$(document).ready(function () {
// Validation works when masks are commented out
$("#PhoneNumber").mask("(999) 999-9999");
$("#ZipCode").mask("99999");