jqueryValidate的具体使用方法很多,这里就不在赘述,这一次只谈一下怎样简单的实现表单验证。

整片文章目的,通过JQvalidation按表单属性配置规则验证,并将验证结果通过poshytip来更灵活的展示在不同的位置。

这里有个问题通过表单标签定义规则不能实现高级复杂的验证,所以复杂的验证还是要通过js去单个实现。

效果如下:

第一步:为表单配置验证属性,实现验证规则的随心所欲。

目的:通过js的引用配置,可以实现对自己定义的标签属性指定json格式的验证规则。

页面引入js

 <script src="~/js/jquery-migrate-1.8.1.min.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.metadata.js"></script>//封装了对validate更简洁的调用

简单验证方式

 <input id="ssp" class="required" />

引用metadata之后就可以所有直接标签定义验证内容和错误消息

重点:这里注意 required 前后的空格,可以查看metadata中的规则,空格是一种书写格式必须保留。

  <input class="{validate:{ required :true},messages:{ required:'请输入名称' }}" id="ssp" />

但这样会影响到表单正常的样式设置,所以可以通过metadata来修改。这样就支持在data-validation属性中使用json格式定义

1.修改metadata中属性初始化

 metadata : {
defaults : {
type: 'attr',
name: 'data-validation',//修改原属性class为data-validation
cre: /({.*})/,
single: 'metadata'
},
setType: function( type, name ){
this.defaults.type = type;
this.defaults.name = name;
}
}

2.修改了这个后发现还是不能用,最后还要修改validation中的class定义

classRules: function(element) {
var rules = {};
var classes = $(element).attr('data-validation');//修改class为要定义的属性
if ( classes ) {
$.each(classes.split(' '), function() {
if (this in $.validator.classRuleSettings) {
$.extend(rules, $.validator.classRuleSettings[this]);
}
});
}
return rules;
},

==有些文档中讲到在文件尾部通过setType来设置,就可以实现,单在我这里并没有得到成功的结果。

$.metadata.setType("attr", "data-validation");

到这里 第一步完成可以实现在表单中通过扩展属性来书写验证规则

第二步:添加poshytip引用实现页面气泡提示效果

目的:完成对验证结果错误消息的接受并进行消息气泡形式显示

这里只添加引用还不行,需要对原版的JS和一些触发事件做一些修改才能完美实现我们想要的功能

<script src="~/plugs/poshytip/jquery.poshytip.js"></script>
<link href="~/plugs/poshytip/tip-twitter/tip-twitter.css" rel="stylesheet" />//样式也是必须要引入的,可以根据配置的皮肤选择引用样式文件

这里引用的jquery.poshytip.js是Poshy Tip jQuery plugin v1.0,针对JQuery1.9以上版本还存在一定的兼容问题出现以下异常

*$.browser这个api从jQuery1.9开始就正式废除,处理错误:Cannot read property ‘msie’ of undefined*/

为了兼容不同的jQuery版本我们需要在文件头加上这样一行代码就可以放心在各个jQuery版本下运行了

jQuery.browser = {}; (function () { jQuery.browser.msie = false; jQuery.browser.version = 0; if (navigator.userAgent.match(/MSIE ([0-9]+)./)) { jQuery.browser.msie = true; jQuery.browser.version = RegExp.$1; } })();

当然引入的样式文件要和配置文件里所配置的皮肤一致

poshytip配置:

$.fn.poshytip.defaults = {
content: '[title]', // content to display ('[title]', 'string', element, function(updateCallback){...}, jQuery)
className: 'tip-twitter', // class for the tips
bgImageFrameSize: 10, // 提示框背景图片的大小
showTimeout: 500, // timeout before showing the tip (in milliseconds 1000 == 1 second)
hideTimeout: 100, // timeout before hiding the tip
showOn: 'hover', // 触发何种事件显示提示框 ('hover', 'focus', 'none') - use 'none' to trigger it manually
alignTo: 'target', // 设置箭头位置('cursor', 'target')
alignX: 'right', // 水平对齐相对于鼠标光标或目标元素
// ('right', 'center', 'left', 'inner-left', 'inner-right') - 'inner-*' matter if alignTo:'target'
alignY: 'center', // 垂直对齐相对于鼠标光标或目标元素
// ('bottom', 'center', 'top', 'inner-bottom', 'inner-top') - 'inner-*' matter if alignTo:'target'
offsetX: 12, // 设置提示框横向偏移 - doesn't matter if alignX:'center'
offsetY: 18, // 设置提示框纵向偏移 - doesn't matter if alignY:'center'
allowTipHover: true, // 当鼠标悬在tip上时,不隐藏tip- matters only if showOn:'hover'
followCursor: false, //提示跟随光标移动- matters only if showOn:'hover' and alignTo:'cursor'
fade: true, // 使用fade动画
slide: true, // 使用slide动画
slideOffset: 8, // slide动画相抵消
showAniDuration: 300, // 显示动画时长 - set to 0 if you don't want show animation
hideAniDuration: 300, // 隐藏动画的持续时间 - set to 0 if you don't want hide animation
refreshAniDuration:1000 //异步更新提示时,动画的持续时间
};

从上边这些配置参数中,我们看到showOn属性有三个值的形式('hover', 'focus', 'none') ,但是这三个值对于原生poshytip来讲挺合理也够用了,但是我们现在需要在发生异常时显示tip的

错误提示,因此需要单独添加一个触发事件就可以解决问题。

这里我们加一个‘elemshow’用于在每个元素验证错误时弹出错误提示,最后一个触发事件为此次需要特别添加,并且制定提示框在3秒后自动消失。

switch (this.opts.showOn) {
case 'hover':
this.$elm.bind({
'mouseenter.poshytip': $.proxy(this.mouseenter, this),
'mouseleave.poshytip': $.proxy(this.mouseleave, this)
});
if (this.opts.alignTo == 'cursor')
this.$elm.bind('mousemove.poshytip', $.proxy(this.mousemove, this));
if (this.opts.allowTipHover)
this.$tip.hover($.proxy(this.clearTimeouts, this), $.proxy(this.hide, this));
break;
case 'focus':
this.$elm.bind({
'focus.poshytip': $.proxy(this.show, this),
'blur.poshytip': $.proxy(this.hide, this)
});
break;
//为指定元素tip提醒——刘自洋20160420
case 'elemshow':
this.showposition();
this.$tip.hover($.proxy(this.clearTimeouts, this), $.proxy(this.hide, this));
setTimeout($.proxy(this.hide, this),3000);
break;
}

到这里对poshytip和validate的改造就完成了,接下来我们考虑一下对于表单的验证,当鼠标离开文本框时或者键盘输入时都会用到验证,这里我们就要对validate的onfocusin,onfocusout

事件进行重写。

 onfocusin: function (element) {
this.lastActive = element;
this.addWrapper(this.errorsFor(element)).hide();
var focusintip = $(element).attr('focusin');
if (focusintip && $(element).parent().children(".focusin").length === 0) {
//$(element).parent().poshytip({ content: focusintip, showOn: 'elemshow' });//tip光标选中后提示
} // Hide error label and remove error class on focus if enabled
if (this.settings.focusCleanup) {
if (this.settings.unhighlight) {
this.settings.unhighlight.call(this, element, this.settings.errorClass, this.settings.validClass);
}
this.hideThese(this.errorsFor(element));
}
}

这里主要实现在用户选择输入框时给出填写提示,但是这东西很多时候也没啥必要,这里就注释掉了。

 /*焦点离开*/
onfocusout: function (element) {
    $(element).valid();
if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {
this.element( element );
}
}

焦点离开时,给出验证并提示验证结果。

设置完触发事件并不能算完事,最重要的是要为错误提示指定显示位置和显示方式进行修改。

  errorPlacement: function (error, element) {//错误信息显示位置
if ($(error).text() != "") {
$(element).poshytip({ content: $(error).text(), showOn: 'elemshow' });
//$(element).parent().poshytip({ content: $(error).text(), showOn: 'elemshow' });//父容器追加提示
}
}

通过前面定义的elemshow这里就派上用场了,错误信息展示。可以根据需要显示在当前目标元素为参照或者设置其父节点为参照。

至于显示位置不要忘了是通过poshytip的默认配置去调整。

第三步:开始验证

目的:为所有只要添加了data-validation标记并添加验证规则的表单做规则验证

为所有form表单添加验证,实现这一功能我们只需在全局js中添加以下代码就可实现。

/*指定表单加上验证才能提交*/
$(function () {
$("form").validate();
});

第四步:代码整理

上边说了这么多其实就是对这些插件的一些修改,看明白了意思就行。下面把各插件完整js奉上,可以直接放入项目应用。

--jquery.validate.js

--修改class属性为自己所定义的验证属性data-validation

/*!
* jQuery Validation Plugin 1.11.1
*
* http://bassistance.de/jquery-plugins/jquery-plugin-validation/
* http://docs.jquery.com/Plugins/Validation
*
* Copyright 2013 Jörn Zaefferer
* Released under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*/ (function ($) { $.extend($.fn, {
// http://docs.jquery.com/Plugins/Validation/validate
validate: function (options) { // if nothing is selected, return nothing; can't chain anyway
if (!this.length) {
if (options && options.debug && window.console) {
console.warn("Nothing selected, can't validate, returning nothing.");
}
return;
} // check if a validator for this form was already created
var validator = $.data(this[0], "validator");
if (validator) {
return validator;
} // Add novalidate tag if HTML5.
this.attr("novalidate", "novalidate"); validator = new $.validator(options, this[0]);
$.data(this[0], "validator", validator); if (validator.settings.onsubmit) { this.validateDelegate(":submit", "click", function (event) {
if (validator.settings.submitHandler) {
validator.submitButton = event.target;
}
// allow suppressing validation by adding a cancel class to the submit button
if ($(event.target).hasClass("cancel")) {
validator.cancelSubmit = true;
} // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
if ($(event.target).attr("formnovalidate") !== undefined) {
validator.cancelSubmit = true;
}
}); // validate the form on submit
this.submit(function (event) {
if (validator.settings.debug) {
// prevent form submit to be able to see console output
event.preventDefault();
}
function handle() {
var hidden;
if (validator.settings.submitHandler) {
if (validator.submitButton) {
// insert a hidden input as a replacement for the missing submit button
hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val($(validator.submitButton).val()).appendTo(validator.currentForm);
}
validator.settings.submitHandler.call(validator, validator.currentForm, event);
if (validator.submitButton) {
// and clean up afterwards; thanks to no-block-scope, hidden can be referenced
hidden.remove();
}
return false;
}
return true;
} // prevent submit for invalid forms or custom submit handlers
if (validator.cancelSubmit) {
validator.cancelSubmit = false;
return handle();
}
if (validator.form()) {
if (validator.pendingRequest) {
validator.formSubmitted = true;
return false;
}
return handle();
} else {
validator.focusInvalid();
return false;
}
});
} return validator;
},
// http://docs.jquery.com/Plugins/Validation/valid
valid: function () {
if ($(this[0]).is("form")) {
return this.validate().form();
} else {
var valid = true;
var validator = $(this[0].form).validate();
this.each(function () {
valid = valid && validator.element(this);
});
return valid;
}
},
// attributes: space seperated list of attributes to retrieve and remove
removeAttrs: function (attributes) {
var result = {},
$element = this;
$.each(attributes.split(/\s/), function (index, value) {
result[value] = $element.attr(value);
$element.removeAttr(value);
});
return result;
},
// http://docs.jquery.com/Plugins/Validation/rules
rules: function (command, argument) {
var element = this[0]; if (command) {
var settings = $.data(element.form, "validator").settings;
var staticRules = settings.rules;
var existingRules = $.validator.staticRules(element);
switch (command) {
case "add":
$.extend(existingRules, $.validator.normalizeRule(argument));
// remove messages from rules, but allow them to be set separetely
delete existingRules.messages;
staticRules[element.name] = existingRules;
if (argument.messages) {
settings.messages[element.name] = $.extend(settings.messages[element.name], argument.messages);
}
break;
case "remove":
if (!argument) {
delete staticRules[element.name];
return existingRules;
}
var filtered = {};
$.each(argument.split(/\s/), function (index, method) {
filtered[method] = existingRules[method];
delete existingRules[method];
});
return filtered;
}
} var data = $.validator.normalizeRules(
$.extend(
{},
$.validator.classRules(element),
$.validator.attributeRules(element),
$.validator.dataRules(element),
$.validator.staticRules(element)
), element); // make sure required is at front
if (data.required) {
var param = data.required;
delete data.required;
data = $.extend({ required: param }, data);
} return data;
}
}); // Custom selectors
$.extend($.expr[":"], {
// http://docs.jquery.com/Plugins/Validation/blank
blank: function (a) { return !$.trim("" + $(a).val()); },
// http://docs.jquery.com/Plugins/Validation/filled
filled: function (a) { return !!$.trim("" + $(a).val()); },
// http://docs.jquery.com/Plugins/Validation/unchecked
unchecked: function (a) { return !$(a).prop("checked"); }
}); // constructor for validator
$.validator = function (options, form) {
this.settings = $.extend(true, {}, $.validator.defaults, options);
this.currentForm = form;
this.init();
}; $.validator.format = function (source, params) {
if (arguments.length === 1) {
return function () {
var args = $.makeArray(arguments);
args.unshift(source);
return $.validator.format.apply(this, args);
};
}
if (arguments.length > 2 && params.constructor !== Array) {
params = $.makeArray(arguments).slice(1);
}
if (params.constructor !== Array) {
params = [params];
}
$.each(params, function (i, n) {
source = source.replace(new RegExp("\\{" + i + "\\}", "g"), function () {
return n;
});
});
return source;
}; $.extend($.validator, { defaults: {
messages: {},
groups: {},
rules: {},
errorClass: "error",
validClass: "valid",
errorElement: "label",
focusInvalid: true,
errorContainer: $([]),
errorLabelContainer: $([]),
onsubmit: true,
ignore: ":hidden",
ignoreTitle: false,
onfocusin: function (element, event) {
this.lastActive = element; // hide error label and remove error class on focus if enabled
if (this.settings.focusCleanup && !this.blockFocusCleanup) {
if (this.settings.unhighlight) {
this.settings.unhighlight.call(this, element, this.settings.errorClass, this.settings.validClass);
}
this.addWrapper(this.errorsFor(element)).hide();
}
},
onfocusout: function (element, event) {
if (!this.checkable(element) && (element.name in this.submitted || !this.optional(element))) {
this.element(element);
}
},
onkeyup: function (element, event) {
if (event.which === 9 && this.elementValue(element) === "") {
return;
} else if (element.name in this.submitted || element === this.lastElement) {
this.element(element);
}
},
onclick: function (element, event) {
// click on selects, radiobuttons and checkboxes
if (element.name in this.submitted) {
this.element(element);
}
// or option elements, check parent select in that case
else if (element.parentNode.name in this.submitted) {
this.element(element.parentNode);
}
},
highlight: function (element, errorClass, validClass) {
if (element.type === "radio") {
this.findByName(element.name).addClass(errorClass).removeClass(validClass);
} else {
$(element).addClass(errorClass).removeClass(validClass);
}
},
unhighlight: function (element, errorClass, validClass) {
if (element.type === "radio") {
this.findByName(element.name).removeClass(errorClass).addClass(validClass);
} else {
$(element).removeClass(errorClass).addClass(validClass);
}
}
}, // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
setDefaults: function (settings) {
$.extend($.validator.defaults, settings);
}, messages: {
required: "This field is required.",
remote: "Please fix this field.",
email: "Please enter a valid email address.",
url: "Please enter a valid URL.",
date: "Please enter a valid date.",
dateISO: "Please enter a valid date (ISO).",
number: "Please enter a valid number.",
digits: "Please enter only digits.",
creditcard: "Please enter a valid credit card number.",
equalTo: "Please enter the same value again.",
maxlength: $.validator.format("Please enter no more than {0} characters."),
minlength: $.validator.format("Please enter at least {0} characters."),
rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
range: $.validator.format("Please enter a value between {0} and {1}."),
max: $.validator.format("Please enter a value less than or equal to {0}."),
min: $.validator.format("Please enter a value greater than or equal to {0}.")
}, autoCreateRanges: false, prototype: { init: function () {
this.labelContainer = $(this.settings.errorLabelContainer);
this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
this.containers = $(this.settings.errorContainer).add(this.settings.errorLabelContainer);
this.submitted = {};
this.valueCache = {};
this.pendingRequest = 0;
this.pending = {};
this.invalid = {};
this.reset(); var groups = (this.groups = {});
$.each(this.settings.groups, function (key, value) {
if (typeof value === "string") {
value = value.split(/\s/);
}
$.each(value, function (index, name) {
groups[name] = key;
});
});
var rules = this.settings.rules;
$.each(rules, function (key, value) {
rules[key] = $.validator.normalizeRule(value);
}); function delegate(event) {
var validator = $.data(this[0].form, "validator"),
eventType = "on" + event.type.replace(/^validate/, "");
if (validator.settings[eventType]) {
validator.settings[eventType].call(validator, this[0], event);
}
}
$(this.currentForm)
.validateDelegate(":text, [type='password'], [type='file'], select, textarea, " +
"[type='number'], [type='search'] ,[type='tel'], [type='url'], " +
"[type='email'], [type='datetime'], [type='date'], [type='month'], " +
"[type='week'], [type='time'], [type='datetime-local'], " +
"[type='range'], [type='color'] ",
"focusin focusout keyup", delegate)
.validateDelegate("[type='radio'], [type='checkbox'], select, option", "click", delegate); if (this.settings.invalidHandler) {
$(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
}
}, // http://docs.jquery.com/Plugins/Validation/Validator/form
form: function () {
this.checkForm();
$.extend(this.submitted, this.errorMap);
this.invalid = $.extend({}, this.errorMap);
if (!this.valid()) {
$(this.currentForm).triggerHandler("invalid-form", [this]);
}
this.showErrors();
return this.valid();
}, checkForm: function () {
this.prepareForm();
for (var i = 0, elements = (this.currentElements = this.elements()) ; elements[i]; i++) {
this.check(elements[i]);
}
return this.valid();
}, // http://docs.jquery.com/Plugins/Validation/Validator/element
element: function (element) {
element = this.validationTargetFor(this.clean(element));
this.lastElement = element;
this.prepareElement(element);
this.currentElements = $(element);
var result = this.check(element) !== false;
if (result) {
delete this.invalid[element.name];
} else {
this.invalid[element.name] = true;
}
if (!this.numberOfInvalids()) {
// Hide error containers on last error
this.toHide = this.toHide.add(this.containers);
}
this.showErrors();
return result;
}, // http://docs.jquery.com/Plugins/Validation/Validator/showErrors
showErrors: function (errors) {
if (errors) {
// add items to error list and map
$.extend(this.errorMap, errors);
this.errorList = [];
for (var name in errors) {
this.errorList.push({
message: errors[name],
element: this.findByName(name)[0]
});
}
// remove items from success list
this.successList = $.grep(this.successList, function (element) {
return !(element.name in errors);
});
}
if (this.settings.showErrors) {
this.settings.showErrors.call(this, this.errorMap, this.errorList);
} else {
this.defaultShowErrors();
}
}, // http://docs.jquery.com/Plugins/Validation/Validator/resetForm
resetForm: function () {
if ($.fn.resetForm) {
$(this.currentForm).resetForm();
}
this.submitted = {};
this.lastElement = null;
this.prepareForm();
this.hideErrors();
this.elements().removeClass(this.settings.errorClass).removeData("previousValue");
}, numberOfInvalids: function () {
return this.objectLength(this.invalid);
}, objectLength: function (obj) {
var count = 0;
for (var i in obj) {
count++;
}
return count;
}, hideErrors: function () {
this.addWrapper(this.toHide).hide();
}, valid: function () {
return this.size() === 0;
}, size: function () {
return this.errorList.length;
}, focusInvalid: function () {
if (this.settings.focusInvalid) {
try {
$(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
.filter(":visible")
.focus()
// manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
.trigger("focusin");
} catch (e) {
// ignore IE throwing errors when focusing hidden elements
}
}
}, findLastActive: function () {
var lastActive = this.lastActive;
return lastActive && $.grep(this.errorList, function (n) {
return n.element.name === lastActive.name;
}).length === 1 && lastActive;
}, elements: function () {
var validator = this,
rulesCache = {}; // select all valid inputs inside the form (no submit or reset buttons)
return $(this.currentForm)
.find("input, select, textarea")
.not(":submit, :reset, :image, [disabled]")
.not(this.settings.ignore)
.filter(function () {
if (!this.name && validator.settings.debug && window.console) {
console.error("%o has no name assigned", this);
} // select only the first element for each name, and only those with rules specified
if (this.name in rulesCache || !validator.objectLength($(this).rules())) {
return false;
} rulesCache[this.name] = true;
return true;
});
}, clean: function (selector) {
return $(selector)[0];
}, errors: function () {
var errorClass = this.settings.errorClass.replace(" ", ".");
return $(this.settings.errorElement + "." + errorClass, this.errorContext);
}, reset: function () {
this.successList = [];
this.errorList = [];
this.errorMap = {};
this.toShow = $([]);
this.toHide = $([]);
this.currentElements = $([]);
}, prepareForm: function () {
this.reset();
this.toHide = this.errors().add(this.containers);
}, prepareElement: function (element) {
this.reset();
this.toHide = this.errorsFor(element);
}, elementValue: function (element) {
var type = $(element).attr("type"),
val = $(element).val(); if (type === "radio" || type === "checkbox") {
return $("input[name='" + $(element).attr("name") + "']:checked").val();
} if (typeof val === "string") {
return val.replace(/\r/g, "");
}
return val;
}, check: function (element) {
element = this.validationTargetFor(this.clean(element)); var rules = $(element).rules();
var dependencyMismatch = false;
var val = this.elementValue(element);
var result; for (var method in rules) {
var rule = { method: method, parameters: rules[method] };
try { result = $.validator.methods[method].call(this, val, element, rule.parameters); // if a method indicates that the field is optional and therefore valid,
// don't mark it as valid when there are no other rules
if (result === "dependency-mismatch") {
dependencyMismatch = true;
continue;
}
dependencyMismatch = false; if (result === "pending") {
this.toHide = this.toHide.not(this.errorsFor(element));
return;
} if (!result) {
this.formatAndAdd(element, rule);
return false;
}
} catch (e) {
if (this.settings.debug && window.console) {
console.log("Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e);
}
throw e;
}
}
if (dependencyMismatch) {
return;
}
if (this.objectLength(rules)) {
this.successList.push(element);
}
return true;
}, // return the custom message for the given element and validation method
// specified in the element's HTML5 data attribute
customDataMessage: function (element, method) {
return $(element).data("msg-" + method.toLowerCase()) || (element.attributes && $(element).attr("data-msg-" + method.toLowerCase()));
}, // return the custom message for the given element name and validation method
customMessage: function (name, method) {
var m = this.settings.messages[name];
return m && (m.constructor === String ? m : m[method]);
}, // return the first defined argument, allowing empty strings
findDefined: function () {
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] !== undefined) {
return arguments[i];
}
}
return undefined;
}, defaultMessage: function (element, method) {
return this.findDefined(
this.customMessage(element.name, method),
this.customDataMessage(element, method),
// title is never undefined, so handle empty string as undefined
!this.settings.ignoreTitle && element.title || undefined,
$.validator.messages[method],
"<strong>Warning: No message defined for " + element.name + "</strong>"
);
}, formatAndAdd: function (element, rule) {
var message = this.defaultMessage(element, rule.method),
theregex = /\$?\{(\d+)\}/g;
if (typeof message === "function") {
message = message.call(this, rule.parameters, element);
} else if (theregex.test(message)) {
message = $.validator.format(message.replace(theregex, "{$1}"), rule.parameters);
}
this.errorList.push({
message: message,
element: element
}); this.errorMap[element.name] = message;
this.submitted[element.name] = message;
}, addWrapper: function (toToggle) {
if (this.settings.wrapper) {
toToggle = toToggle.add(toToggle.parent(this.settings.wrapper));
}
return toToggle;
}, defaultShowErrors: function () {
var i, elements;
for (i = 0; this.errorList[i]; i++) {
var error = this.errorList[i];
if (this.settings.highlight) {
this.settings.highlight.call(this, error.element, this.settings.errorClass, this.settings.validClass);
}
this.showLabel(error.element, error.message);
}
if (this.errorList.length) {
this.toShow = this.toShow.add(this.containers);
}
if (this.settings.success) {
for (i = 0; this.successList[i]; i++) {
this.showLabel(this.successList[i]);
}
}
if (this.settings.unhighlight) {
for (i = 0, elements = this.validElements() ; elements[i]; i++) {
this.settings.unhighlight.call(this, elements[i], this.settings.errorClass, this.settings.validClass);
}
}
this.toHide = this.toHide.not(this.toShow);
this.hideErrors();
this.addWrapper(this.toShow).show();
}, validElements: function () {
return this.currentElements.not(this.invalidElements());
}, invalidElements: function () {
return $(this.errorList).map(function () {
return this.element;
});
}, showLabel: function (element, message) {
var label = this.errorsFor(element);
if (label.length) {
// refresh error/success class
label.removeClass(this.settings.validClass).addClass(this.settings.errorClass);
// replace message on existing label
label.html(message);
} else {
// create label
label = $("<" + this.settings.errorElement + ">")
.attr("for", this.idOrName(element))
.addClass(this.settings.errorClass)
.html(message || "");
if (this.settings.wrapper) {
// make sure the element is visible, even in IE
// actually showing the wrapped element is handled elsewhere
label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
}
if (!this.labelContainer.append(label).length) {
if (this.settings.errorPlacement) {
this.settings.errorPlacement(label, $(element));
} else {
label.insertAfter(element);
}
}
}
if (!message && this.settings.success) {
label.text("");
if (typeof this.settings.success === "string") {
label.addClass(this.settings.success);
} else {
this.settings.success(label, element);
}
}
this.toShow = this.toShow.add(label);
}, errorsFor: function (element) {
var name = this.idOrName(element);
return this.errors().filter(function () {
return $(this).attr("for") === name;
});
}, idOrName: function (element) {
return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
}, validationTargetFor: function (element) {
// if radio/checkbox, validate first element in group instead
if (this.checkable(element)) {
element = this.findByName(element.name).not(this.settings.ignore)[0];
}
return element;
}, checkable: function (element) {
return (/radio|checkbox/i).test(element.type);
}, findByName: function (name) {
return $(this.currentForm).find("[name='" + name + "']");
}, getLength: function (value, element) {
switch (element.nodeName.toLowerCase()) {
case "select":
return $("option:selected", element).length;
case "input":
if (this.checkable(element)) {
return this.findByName(element.name).filter(":checked").length;
}
}
return value.length;
}, depend: function (param, element) {
return this.dependTypes[typeof param] ? this.dependTypes[typeof param](param, element) : true;
}, dependTypes: {
"boolean": function (param, element) {
return param;
},
"string": function (param, element) {
return !!$(param, element.form).length;
},
"function": function (param, element) {
return param(element);
}
}, optional: function (element) {
var val = this.elementValue(element);
return !$.validator.methods.required.call(this, val, element) && "dependency-mismatch";
}, startRequest: function (element) {
if (!this.pending[element.name]) {
this.pendingRequest++;
this.pending[element.name] = true;
}
}, stopRequest: function (element, valid) {
this.pendingRequest--;
// sometimes synchronization fails, make sure pendingRequest is never < 0
if (this.pendingRequest < 0) {
this.pendingRequest = 0;
}
delete this.pending[element.name];
if (valid && this.pendingRequest === 0 && this.formSubmitted && this.form()) {
$(this.currentForm).submit();
this.formSubmitted = false;
} else if (!valid && this.pendingRequest === 0 && this.formSubmitted) {
$(this.currentForm).triggerHandler("invalid-form", [this]);
this.formSubmitted = false;
}
}, previousValue: function (element) {
return $.data(element, "previousValue") || $.data(element, "previousValue", {
old: null,
valid: true,
message: this.defaultMessage(element, "remote")
});
} }, classRuleSettings: {
required: { required: true },
email: { email: true },
url: { url: true },
date: { date: true },
dateISO: { dateISO: true },
number: { number: true },
digits: { digits: true },
creditcard: { creditcard: true }
}, addClassRules: function (className, rules) {
if (className.constructor === String) {
this.classRuleSettings[className] = rules;
} else {
$.extend(this.classRuleSettings, className);
}
}, classRules: function (element) {
var rules = {};
var classes = $(element).attr("data-validation");
if (classes) {
$.each(classes.split(" "), function () {
if (this in $.validator.classRuleSettings) {
$.extend(rules, $.validator.classRuleSettings[this]);
}
});
}
return rules;
}, attributeRules: function (element) {
var rules = {};
var $element = $(element);
var type = $element[0].getAttribute("type"); for (var method in $.validator.methods) {
var value; // support for <input required> in both html5 and older browsers
if (method === "required") {
value = $element.get(0).getAttribute(method);
// Some browsers return an empty string for the required attribute
// and non-HTML5 browsers might have required="" markup
if (value === "") {
value = true;
}
// force non-HTML5 browsers to return bool
value = !!value;
} else {
value = $element.attr(method);
} // convert the value to a number for number inputs, and for text for backwards compability
// allows type="date" and others to be compared as strings
if (/min|max/.test(method) && (type === null || /number|range|text/.test(type))) {
value = Number(value);
} if (value) {
rules[method] = value;
} else if (type === method && type !== 'range') {
// exception: the jquery validate 'range' method
// does not test for the html5 'range' type
rules[method] = true;
}
} // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
delete rules.maxlength;
} return rules;
}, dataRules: function (element) {
var method, value,
rules = {}, $element = $(element);
for (method in $.validator.methods) {
value = $element.data("rule-" + method.toLowerCase());
if (value !== undefined) {
rules[method] = value;
}
}
return rules;
}, staticRules: function (element) {
var rules = {};
var validator = $.data(element.form, "validator");
if (validator.settings.rules) {
rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
}
return rules;
}, normalizeRules: function (rules, element) {
// handle dependency check
$.each(rules, function (prop, val) {
// ignore rule when param is explicitly false, eg. required:false
if (val === false) {
delete rules[prop];
return;
}
if (val.param || val.depends) {
var keepRule = true;
switch (typeof val.depends) {
case "string":
keepRule = !!$(val.depends, element.form).length;
break;
case "function":
keepRule = val.depends.call(element, element);
break;
}
if (keepRule) {
rules[prop] = val.param !== undefined ? val.param : true;
} else {
delete rules[prop];
}
}
}); // evaluate parameters
$.each(rules, function (rule, parameter) {
rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
}); // clean number parameters
$.each(['minlength', 'maxlength'], function () {
if (rules[this]) {
rules[this] = Number(rules[this]);
}
});
$.each(['rangelength', 'range'], function () {
var parts;
if (rules[this]) {
if ($.isArray(rules[this])) {
rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
} else if (typeof rules[this] === "string") {
parts = rules[this].split(/[\s,]+/);
rules[this] = [Number(parts[0]), Number(parts[1])];
}
}
}); if ($.validator.autoCreateRanges) {
// auto-create ranges
if (rules.min && rules.max) {
rules.range = [rules.min, rules.max];
delete rules.min;
delete rules.max;
}
if (rules.minlength && rules.maxlength) {
rules.rangelength = [rules.minlength, rules.maxlength];
delete rules.minlength;
delete rules.maxlength;
}
} return rules;
}, // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
normalizeRule: function (data) {
if (typeof data === "string") {
var transformed = {};
$.each(data.split(/\s/), function () {
transformed[this] = true;
});
data = transformed;
}
return data;
}, // http://docs.jquery.com/Plugins/Validation/Validator/addMethod
addMethod: function (name, method, message) {
$.validator.methods[name] = method;
$.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];
if (method.length < 3) {
$.validator.addClassRules(name, $.validator.normalizeRule(name));
}
}, methods: { // http://docs.jquery.com/Plugins/Validation/Methods/required
required: function (value, element, param) {
// check if dependency is met
if (!this.depend(param, element)) {
return "dependency-mismatch";
}
if (element.nodeName.toLowerCase() === "select") {
// could be an array for select-multiple or a string, both are fine this way
var val = $(element).val();
return val && val.length > 0;
}
if (this.checkable(element)) {
return this.getLength(value, element) > 0;
}
return $.trim(value).length > 0;
}, // http://docs.jquery.com/Plugins/Validation/Methods/email
email: function (value, element) {
// contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(value);
}, // http://docs.jquery.com/Plugins/Validation/Methods/url
url: function (value, element) {
// contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
return this.optional(element) || /^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
}, // http://docs.jquery.com/Plugins/Validation/Methods/date
date: function (value, element) {
return this.optional(element) || !/Invalid|NaN/.test(new Date(value).toString());
}, // http://docs.jquery.com/Plugins/Validation/Methods/dateISO
dateISO: function (value, element) {
return this.optional(element) || /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(value);
}, // http://docs.jquery.com/Plugins/Validation/Methods/number
number: function (value, element) {
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
}, // http://docs.jquery.com/Plugins/Validation/Methods/digits
digits: function (value, element) {
return this.optional(element) || /^\d+$/.test(value);
}, // http://docs.jquery.com/Plugins/Validation/Methods/creditcard
// based on http://en.wikipedia.org/wiki/Luhn
creditcard: function (value, element) {
if (this.optional(element)) {
return "dependency-mismatch";
}
// accept only spaces, digits and dashes
if (/[^0-9 \-]+/.test(value)) {
return false;
}
var nCheck = 0,
nDigit = 0,
bEven = false; value = value.replace(/\D/g, ""); for (var n = value.length - 1; n >= 0; n--) {
var cDigit = value.charAt(n);
nDigit = parseInt(cDigit, 10);
if (bEven) {
if ((nDigit *= 2) > 9) {
nDigit -= 9;
}
}
nCheck += nDigit;
bEven = !bEven;
} return (nCheck % 10) === 0;
}, // http://docs.jquery.com/Plugins/Validation/Methods/minlength
minlength: function (value, element, param) {
var length = $.isArray(value) ? value.length : this.getLength($.trim(value), element);
return this.optional(element) || length >= param;
}, // http://docs.jquery.com/Plugins/Validation/Methods/maxlength
maxlength: function (value, element, param) {
var length = $.isArray(value) ? value.length : this.getLength($.trim(value), element);
return this.optional(element) || length <= param;
}, // http://docs.jquery.com/Plugins/Validation/Methods/rangelength
rangelength: function (value, element, param) {
var length = $.isArray(value) ? value.length : this.getLength($.trim(value), element);
return this.optional(element) || (length >= param[0] && length <= param[1]);
}, // http://docs.jquery.com/Plugins/Validation/Methods/min
min: function (value, element, param) {
return this.optional(element) || value >= param;
}, // http://docs.jquery.com/Plugins/Validation/Methods/max
max: function (value, element, param) {
return this.optional(element) || value <= param;
}, // http://docs.jquery.com/Plugins/Validation/Methods/range
range: function (value, element, param) {
return this.optional(element) || (value >= param[0] && value <= param[1]);
}, // http://docs.jquery.com/Plugins/Validation/Methods/equalTo
equalTo: function (value, element, param) {
// bind to the blur event of the target in order to revalidate whenever the target field is updated
// TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
var target = $(param);
if (this.settings.onfocusout) {
target.unbind(".validate-equalTo").bind("blur.validate-equalTo", function () {
$(element).valid();
});
}
return value === target.val();
}, // http://docs.jquery.com/Plugins/Validation/Methods/remote
remote: function (value, element, param) {
if (this.optional(element)) {
return "dependency-mismatch";
} var previous = this.previousValue(element);
if (!this.settings.messages[element.name]) {
this.settings.messages[element.name] = {};
}
previous.originalMessage = this.settings.messages[element.name].remote;
this.settings.messages[element.name].remote = previous.message; param = typeof param === "string" && { url: param } || param; if (previous.old === value) {
return previous.valid;
} previous.old = value;
var validator = this;
this.startRequest(element);
var data = {};
data[element.name] = value;
$.ajax($.extend(true, {
url: param,
mode: "abort",
port: "validate" + element.name,
dataType: "json",
data: data,
success: function (response) {
validator.settings.messages[element.name].remote = previous.originalMessage;
var valid = response === true || response === "true";
if (valid) {
var submitted = validator.formSubmitted;
validator.prepareElement(element);
validator.formSubmitted = submitted;
validator.successList.push(element);
delete validator.invalid[element.name];
validator.showErrors();
} else {
var errors = {};
var message = response || validator.defaultMessage(element, "remote");
errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
validator.invalid[element.name] = true;
validator.showErrors(errors);
}
previous.valid = valid;
validator.stopRequest(element, valid);
}
}, param));
return "pending";
} } }); // deprecated, use $.validator.format instead
$.format = $.validator.format; }(jQuery)); // ajax mode: abort
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
(function ($) {
var pendingRequests = {};
// Use a prefilter if available (1.5+)
if ($.ajaxPrefilter) {
$.ajaxPrefilter(function (settings, _, xhr) {
var port = settings.port;
if (settings.mode === "abort") {
if (pendingRequests[port]) {
pendingRequests[port].abort();
}
pendingRequests[port] = xhr;
}
});
} else {
// Proxy ajax
var ajax = $.ajax;
$.ajax = function (settings) {
var mode = ("mode" in settings ? settings : $.ajaxSettings).mode,
port = ("port" in settings ? settings : $.ajaxSettings).port;
if (mode === "abort") {
if (pendingRequests[port]) {
pendingRequests[port].abort();
}
pendingRequests[port] = ajax.apply(this, arguments);
return pendingRequests[port];
}
return ajax.apply(this, arguments);
};
}
}(jQuery)); // provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
(function ($) {
$.extend($.fn, {
validateDelegate: function (delegate, type, handler) {
return this.bind(type, function (event) {
var target = $(event.target);
if (target.is(delegate)) {
return handler.apply(target, arguments);
}
});
}
});
}(jQuery));

jquery.validate.js

这里的poshytip的皮肤文件不做任何修改,下载后可以直接引用,需要修改样式的单独修改poshytip对应皮肤文件吧。

--jquery.poshytip.js

--修改并添加对版本js的兼容

--为showOn添加elemshow可以在验证错误时显示错误消息提示。

/*
* 修改时间:2016-04-20
*修改人:Loyung
*说明:
*1.更新对JQuery9以上版本的兼容问题
*2.参数showOn添加"elemshow",用作poshytip处理时使用。可以为任意元素创建showOn动作。
*/
/*
* Poshy Tip jQuery plugin v1.0
* http://vadikom.com/tools/poshy-tip-jquery-plugin-for-stylish-tooltips/
* Copyright 2010, Vasil Dinkov, http://vadikom.com/
*/
/*$.browser这个api从jQuery1.9开始就正式废除,处理错误:Cannot read property ‘msie’ of undefined*/
jQuery.browser = {}; (function () { jQuery.browser.msie = false; jQuery.browser.version = 0; if (navigator.userAgent.match(/MSIE ([0-9]+)./)) { jQuery.browser.msie = true; jQuery.browser.version = RegExp.$1; } })(); (function($) { var tips = [],
reBgImage = /^url\(["']?([^"'\)]*)["']?\);?$/i,
rePNG = /\.png$/i,
ie6 = $.browser.msie && $.browser.version == 6; // make sure the tips' position is updated on resize
function handleWindowResize() {
$.each(tips, function() {
this.refresh(true);
});
}
$(window).resize(handleWindowResize); $.Poshytip = function (elm, options) {
this.$elm = $(elm);
this.opts = $.extend({}, $.fn.poshytip.defaults, options);
this.$tip = $(['<div class="',this.opts.className,'">',
'<div class="tip-inner tip-bg-image"></div>',
'<div class="tip-arrow tip-arrow-top tip-arrow-right tip-arrow-bottom tip-arrow-left"></div>',
'</div>'].join(''));
this.$arrow = this.$tip.find('div.tip-arrow');
this.$inner = this.$tip.find('div.tip-inner');
this.disabled = false;
this.init();
}; $.Poshytip.prototype = {
init: function() {
tips.push(this); // save the original title and a reference to the Poshytip object
this.$elm.data('title.poshytip', this.$elm.attr('title'))
.data('poshytip', this); // hook element events
switch (this.opts.showOn) {
case 'hover':
this.$elm.bind({
'mouseenter.poshytip': $.proxy(this.mouseenter, this),
'mouseleave.poshytip': $.proxy(this.mouseleave, this)
});
if (this.opts.alignTo == 'cursor')
this.$elm.bind('mousemove.poshytip', $.proxy(this.mousemove, this));
if (this.opts.allowTipHover)
this.$tip.hover($.proxy(this.clearTimeouts, this), $.proxy(this.hide, this));
break;
case 'focus':
this.$elm.bind({
'focus.poshytip': $.proxy(this.show, this),
'blur.poshytip': $.proxy(this.hide, this)
});
break;
//为指定元素tip提醒——刘自洋20160420
case 'elemshow':
this.showposition();
this.$tip.hover($.proxy(this.clearTimeouts, this), $.proxy(this.hide, this));
setTimeout($.proxy(this.hide, this),3000);
break;
}
},
mouseenter: function(e) {
if (this.disabled)
return true; this.clearTimeouts();
this.$elm.attr('title', '');
this.showTimeout = setTimeout($.proxy(this.show, this), this.opts.showTimeout);
},
mouseleave: function() {
if (this.disabled)
return true; this.clearTimeouts();
this.$elm.attr('title', this.$elm.data('title.poshytip'));
this.hideTimeout = setTimeout($.proxy(this.hide, this), this.opts.hideTimeout);
},
mousemove: function(e) {
if (this.disabled)
return true; this.eventX = e.pageX;
this.eventY = e.pageY;
if (this.opts.followCursor && this.$tip.data('active')) {
this.calcPos();
this.$tip.css({left: this.pos.l, top: this.pos.t});
if (this.pos.arrow)
this.$arrow[0].className = 'tip-arrow tip-arrow-' + this.pos.arrow;
}
},
showposition: function (e) {
if (this.disabled || this.$tip.data('active'))
return; this.opts.alignTo = 'target';
this.calcPos();
this.update(); this.display();
},
show: function() {
if (this.disabled || this.$tip.data('active'))
return; this.reset();
this.update();
this.display();
},
hide: function() {
if (this.disabled || !this.$tip.data('active'))
return; this.display(true);
},
reset: function() {
this.$tip.queue([]).detach().css('visibility', 'hidden').data('active', false);
this.$inner.find('*').poshytip('hide');
if (this.opts.fade)
this.$tip.css('opacity', this.opacity);
this.$arrow[0].className = 'tip-arrow tip-arrow-top tip-arrow-right tip-arrow-bottom tip-arrow-left';
},
update: function(content) {
if (this.disabled)
return; var async = content !== undefined;
if (async) {
if (!this.$tip.data('active'))
return;
} else {
content = this.opts.content;
} this.$inner.contents().detach();
var self = this;
this.$inner.append(
typeof content == 'function' ?
content.call(this.$elm[0], function(newContent) {
self.update(newContent);
}) :
content == '[title]' ? this.$elm.data('title.poshytip') : content
); this.refresh(async);
},
refresh: function(async) {
if (this.disabled)
return; if (async) {
if (!this.$tip.data('active'))
return;
// save current position as we will need to animate
var currPos = {left: this.$tip.css('left'), top: this.$tip.css('top')};
} // reset position to avoid text wrapping, etc.
this.$tip.css({left: 0, top: 0}).appendTo(document.body); // save default opacity
if (this.opacity === undefined)
this.opacity = this.$tip.css('opacity'); // check for images - this code is here (i.e. executed each time we show the tip and not on init) due to some browser inconsistencies
var bgImage = this.$tip.css('background-image').match(reBgImage),
arrow = this.$arrow.css('background-image').match(reBgImage); if (bgImage) {
var bgImagePNG = rePNG.test(bgImage[1]);
// fallback to background-color/padding/border in IE6 if a PNG is used
if (ie6 && bgImagePNG) {
this.$tip.css('background-image', 'none');
this.$inner.css({margin: 0, border: 0, padding: 0});
bgImage = bgImagePNG = false;
} else {
this.$tip.prepend('<table border="0" cellpadding="0" cellspacing="0"><tr><td class="tip-top tip-bg-image" colspan="2"><span></span></td><td class="tip-right tip-bg-image" rowspan="2"><span></span></td></tr><tr><td class="tip-left tip-bg-image" rowspan="2"><span></span></td><td></td></tr><tr><td class="tip-bottom tip-bg-image" colspan="2"><span></span></td></tr></table>')
.css({border: 0, padding: 0, 'background-image': 'none', 'background-color': 'transparent'})
.find('.tip-bg-image').css('background-image', 'url("' + bgImage[1] +'")').end()
.find('td').eq(3).append(this.$inner);
}
// disable fade effect in IE due to Alpha filter + translucent PNG issue
if (bgImagePNG && !$.support.opacity)
this.opts.fade = false;
}
// IE arrow fixes
if (arrow && !$.support.opacity) {
// disable arrow in IE6 if using a PNG
if (ie6 && rePNG.test(arrow[1])) {
arrow = false;
this.$arrow.css('background-image', 'none');
}
// disable fade effect in IE due to Alpha filter + translucent PNG issue
this.opts.fade = false;
} var $table = this.$tip.find('table');
if (ie6) {
// fix min/max-width in IE6
this.$tip[0].style.width = '';
$table.width('auto').find('td').eq(3).width('auto');
var tipW = this.$tip.width(),
minW = parseInt(this.$tip.css('min-width')),
maxW = parseInt(this.$tip.css('max-width'));
if (!isNaN(minW) && tipW < minW)
tipW = minW;
else if (!isNaN(maxW) && tipW > maxW)
tipW = maxW;
this.$tip.add($table).width(tipW).eq(0).find('td').eq(3).width('100%');
} else if ($table[0]) {
// fix the table width if we are using a background image
$table.width('auto').find('td').eq(3).width('auto').end().end().width(this.$tip.width()).find('td').eq(3).width('100%');
}
this.tipOuterW = this.$tip.outerWidth();
this.tipOuterH = this.$tip.outerHeight(); this.calcPos(); // position and show the arrow image
if (arrow && this.pos.arrow) {
this.$arrow[0].className = 'tip-arrow tip-arrow-' + this.pos.arrow;
this.$arrow.css('visibility', 'inherit');
} if (async)
this.$tip.css(currPos).animate({left: this.pos.l, top: this.pos.t}, 200);
else
this.$tip.css({left: this.pos.l, top: this.pos.t});
},
display: function(hide) {
var active = this.$tip.data('active');
if (active && !hide || !active && hide)
return; this.$tip.stop();
if ((this.opts.slide && this.pos.arrow || this.opts.fade) && (hide && this.opts.hideAniDuration || !hide && this.opts.showAniDuration)) {
var from = {}, to = {};
// this.pos.arrow is only undefined when alignX == alignY == 'center' and we don't need to slide in that rare case
if (this.opts.slide && this.pos.arrow) {
var prop, arr;
if (this.pos.arrow == 'bottom' || this.pos.arrow == 'top') {
prop = 'top';
arr = 'bottom';
} else {
prop = 'left';
arr = 'right';
}
var val = parseInt(this.$tip.css(prop));
from[prop] = val + (hide ? 0 : this.opts.slideOffset * (this.pos.arrow == arr ? -1 : 1));
to[prop] = val + (hide ? this.opts.slideOffset * (this.pos.arrow == arr ? 1 : -1) : 0);
}
if (this.opts.fade) {
from.opacity = hide ? this.$tip.css('opacity') : 0;
to.opacity = hide ? 0 : this.opacity;
}
this.$tip.css(from).animate(to, this.opts[hide ? 'hideAniDuration' : 'showAniDuration']);
}
hide ? this.$tip.queue($.proxy(this.reset, this)) : this.$tip.css('visibility', 'inherit');
this.$tip.data('active', !active);
},
disable: function() {
this.reset();
this.disabled = true;
},
enable: function() {
this.disabled = false;
},
destroy: function() {
this.reset();
this.$tip.remove();
this.$elm.unbind('poshytip').removeData('title.poshytip').removeData('poshytip');
tips.splice($.inArray(this, tips), 1);
},
clearTimeouts: function() {
if (this.showTimeout) {
clearTimeout(this.showTimeout);
this.showTimeout = 0;
}
if (this.hideTimeout) {
clearTimeout(this.hideTimeout);
this.hideTimeout = 0;
}
},
calcPos: function() {
var pos = {l: 0, t: 0, arrow: ''},
$win = $(window),
win = {
l: $win.scrollLeft(),
t: $win.scrollTop(),
w: $win.width(),
h: $win.height()
}, xL, xC, xR, yT, yC, yB;
if (this.opts.alignTo == 'cursor') {
xL = xC = xR = this.eventX;
yT = yC = yB = this.eventY;
} else { // this.opts.alignTo == 'target'
var elmOffset = this.$elm.offset(),
elm = {
l: elmOffset.left,
t: elmOffset.top,
w: this.$elm.outerWidth(),
h: this.$elm.outerHeight()
};
xL = elm.l + (this.opts.alignX != 'inner-right' ? 0 : elm.w); // left edge
xC = xL + Math.floor(elm.w / 2); // h center
xR = xL + (this.opts.alignX != 'inner-left' ? elm.w : 0); // right edge
yT = elm.t + (this.opts.alignY != 'inner-bottom' ? 0 : elm.h); // top edge
yC = yT + Math.floor(elm.h / 2); // v center
yB = yT + (this.opts.alignY != 'inner-top' ? elm.h : 0); // bottom edge
} // keep in viewport and calc arrow position
switch (this.opts.alignX) {
case 'right':
case 'inner-left':
pos.l = xR + this.opts.offsetX;
if (pos.l + this.tipOuterW > win.l + win.w)
pos.l = win.l + win.w - this.tipOuterW;
if (this.opts.alignX == 'right' || this.opts.alignY == 'center')
pos.arrow = 'left';
break;
case 'center':
pos.l = xC - Math.floor(this.tipOuterW / 2);
if (pos.l + this.tipOuterW > win.l + win.w)
pos.l = win.l + win.w - this.tipOuterW;
else if (pos.l < win.l)
pos.l = win.l;
break;
default: // 'left' || 'inner-right'
pos.l = xL - this.tipOuterW - this.opts.offsetX;
if (pos.l < win.l)
pos.l = win.l;
if (this.opts.alignX == 'left' || this.opts.alignY == 'center')
pos.arrow = 'right';
}
switch (this.opts.alignY) {
case 'bottom':
case 'inner-top':
pos.t = yB + this.opts.offsetY;
// 'left' and 'right' need priority for 'target'
if (!pos.arrow || this.opts.alignTo == 'cursor')
pos.arrow = 'top';
if (pos.t + this.tipOuterH > win.t + win.h) {
pos.t = yT - this.tipOuterH - this.opts.offsetY;
if (pos.arrow == 'top')
pos.arrow = 'bottom';
}
break;
case 'center':
pos.t = yC - Math.floor(this.tipOuterH / 2);
if (pos.t + this.tipOuterH > win.t + win.h)
pos.t = win.t + win.h - this.tipOuterH;
else if (pos.t < win.t)
pos.t = win.t;
break;
default: // 'top' || 'inner-bottom'
pos.t = yT - this.tipOuterH - this.opts.offsetY;
// 'left' and 'right' need priority for 'target'
if (!pos.arrow || this.opts.alignTo == 'cursor')
pos.arrow = 'bottom';
if (pos.t < win.t) {
pos.t = yB + this.opts.offsetY;
if (pos.arrow == 'bottom')
pos.arrow = 'top';
}
}
this.pos = pos;
}
}; $.fn.poshytip = function(options){
if (typeof options == 'string') {
return this.each(function() {
var poshytip = $(this).data('poshytip');
if (poshytip && poshytip[options])
poshytip[options]();
});
} var opts = $.extend({}, $.fn.poshytip.defaults, options); // generate CSS for this tip class if not already generated
if (!$('#poshytip-css-' + opts.className)[0])
$(['<style id="poshytip-css-',opts.className,'" type="text/css">',
'div.',opts.className,'{visibility:hidden;position:absolute;top:0;left:0;}',
'div.',opts.className,' table, div.',opts.className,' td{margin:0;font-family:inherit;font-size:inherit;font-weight:inherit;font-style:inherit;font-variant:inherit;}',
'div.',opts.className,' td.tip-bg-image span{display:block;font:1px/1px sans-serif;height:',opts.bgImageFrameSize,'px;width:',opts.bgImageFrameSize,'px;overflow:hidden;}',
'div.',opts.className,' td.tip-right{background-position:100% 0;}',
'div.',opts.className,' td.tip-bottom{background-position:100% 100%;}',
'div.',opts.className,' td.tip-left{background-position:0 100%;}',
'div.',opts.className,' div.tip-inner{background-position:-',opts.bgImageFrameSize,'px -',opts.bgImageFrameSize,'px;}',
'div.',opts.className,' div.tip-arrow{visibility:hidden;position:absolute;overflow:hidden;font:1px/1px sans-serif;}',
'</style>'].join('')).appendTo('head'); return this.each(function() {
new $.Poshytip(this, opts);
});
} // default settings
$.fn.poshytip.defaults = {
content: '[title]', // content to display ('[title]', 'string', element, function(updateCallback){...}, jQuery)
className: 'tip-twitter', // class for the tips
bgImageFrameSize: 10, // 提示框背景图片的大小
showTimeout: 500, // timeout before showing the tip (in milliseconds 1000 == 1 second)
hideTimeout: 100, // timeout before hiding the tip
showOn: 'hover', // 触发何种事件显示提示框 ('hover', 'focus', 'none') - use 'none' to trigger it manually
alignTo: 'target', // 设置箭头位置('cursor', 'target')
alignX: 'right', // 水平对齐相对于鼠标光标或目标元素
// ('right', 'center', 'left', 'inner-left', 'inner-right') - 'inner-*' matter if alignTo:'target'
alignY: 'center', // 垂直对齐相对于鼠标光标或目标元素
// ('bottom', 'center', 'top', 'inner-bottom', 'inner-top') - 'inner-*' matter if alignTo:'target'
offsetX: 12, // 设置提示框横向偏移 - doesn't matter if alignX:'center'
offsetY: 18, // 设置提示框纵向偏移 - doesn't matter if alignY:'center'
allowTipHover: true, // 当鼠标悬在tip上时,不隐藏tip- matters only if showOn:'hover'
followCursor: false, //提示跟随光标移动- matters only if showOn:'hover' and alignTo:'cursor'
fade: true, // 使用fade动画
slide: true, // 使用slide动画
slideOffset: 8, // slide动画相抵消
showAniDuration: 300, // 显示动画时长 - set to 0 if you don't want show animation
hideAniDuration: 300, // 隐藏动画的持续时间 - set to 0 if you don't want hide animation
refreshAniDuration:1000 //异步更新提示时,动画的持续时间
}; })(jQuery);

jquery.poshytip.js

我们自己重写定义的一些方法在这里比较综合,但也是结合validation和poshytip的关键。

--jquery.validate.tip.js

/*
*创建人:刘自洋
*创建时间:2017-07-11
*说明:该文件对jquery.validate几种验证事件进行重配置,以便于做消息Tip提醒。是validate与poshytip结合的关键
*/
$.validator.setDefaults({
/*关闭键盘输入时的实时校验*/
onkeyup: function (element) {
//$(element).poshytip({ content: "键盘输入", showOn: 'elemshow' });
},
/*校验成功/
success: function (element) {
//$(element).poshytip({ content: "校验成功", showOn: 'elemshow' });
},
/*获得焦点后执行*/
onfocusin: function (element) {
this.lastActive = element;
this.addWrapper(this.errorsFor(element)).hide();
var focusintip = $(element).attr('focusin');
if (focusintip && $(element).parent().children(".focusin").length === 0) {
//$(element).parent().poshytip({ content: focusintip, showOn: 'elemshow' });//tip光标选中后提示
} // Hide error label and remove error class on focus if enabled
if (this.settings.focusCleanup) {
if (this.settings.unhighlight) {
this.settings.unhighlight.call(this, element, this.settings.errorClass, this.settings.validClass);
}
this.hideThese(this.errorsFor(element));
}
},
/*焦点离开*/
onfocusout: function (element) {
$(element).valid();
if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {
this.element( element );
}
},
errorPlacement: function (error, element) {//错误信息显示位置
if ($(error).text() != "") {
$(element).poshytip({ content: $(error).text(), showOn: 'elemshow' });
//$(element).parent().poshytip({ content: $(error).text(), showOn: 'elemshow' });//父容器追加提示
}
},
submitHandler: function (form) {//表单验证完成后提交表单
form.submit();
return false;
}
});
/*指定表单加上验证才能提交*/
$(function () {
$("form").validate();
});
/*修改默认validate提示信息*/
$.extend($.validator.messages, {
required: "必填字段",
remote: "请修正该字段",
email: "请输入正确格式的电子邮件",
url: "请输入合法的网址",
date: "请输入合法的日期",
dateISO: "请输入合法的日期 (ISO).",
number: "请输入合法的数字",
digits: "只能输入整数",
creditcard: "请输入合法的信用卡号",
equalTo: "请再次输入相同的值",
accept: "请输入拥有合法后缀名的字符串",
maxlength: $.validator.format("请输入一个长度最多是 {0} 的字符串"),
minlength: $.validator.format("请输入一个长度最少是 {0} 的字符串"),
rangelength: $.validator.format("请输入一个长度介于 {0} 和 {1} 之间的字符串"),
range: $.validator.format("请输入一个介于 {0} 和 {1} 之间的值"),
max: $.validator.format("请输入一个最大为 {0} 的值"),
min: $.validator.format("请输入一个最小为 {0} 的值"),
});

jquery.validate.tip.js

到这里已经基本算是完成了,后面会陆续优化一些bug。

疑难杂症分析:

1.如果提交表单时出现只验证第一个设置验证的元素,那么请检查标签是否配置了name属性或者配置了name属性相同了。

如果大家遇到哪些存在的bug都可以交流修改。

关于validation的具体详解对于新手来说可能好需要进一步了解,可以查看REX HE的博客 jQuery验证控件jquery.validate.js使用说明+中文API

使用jquery.validation+jquery.poshytip做表单验证--未完待续的更多相关文章

  1. 使用jquery插件validate制作的表单验证案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. jQuery应用实例5:表单验证

    1.validation插件的使用: 入门案例: <html> <head> <meta charset="UTF-8"> <title& ...

  3. jQuery基础(常用插件 表单验证,图片放大镜,自定义对象级,jQuery UI,面板折叠)

    1.表单验证插件——validate   该插件自带包含必填.数字.URL在内容的验证规则,即时显示异常信息,此外,还允许自定义验证规则,插件调用方法如下:   $(form).validate({o ...

  4. 【表单验证】基于jQuery的高度灵活的表单验证(无UI)

    表单验证是前端开发过程中常见的一个需求,产品需求.业务逻辑的不同,表单验证的方式方法也有所区别.而最重要的是我们要清楚,表单验证的核心原则是--错误信息提示准确,并且尽可能少的打扰/干扰用户的输入和体 ...

  5. 使用jquery validate结合zui作表单验证

    1.引入jquery validate和zui <!-- jQuery (ZUI中的Javascript组件依赖于jQuery) --> <script src="${_b ...

  6. 用jquery写自己的form表单验证

    这几天看了锋利的jquery,感觉很不错.特别是jquery强大的选择器.今天就利用jquery写了一个自己的form表单验证的小案例.当巩固下jquery.首先贴下代码,当然只是一个小案例. 思路: ...

  7. JQuery制作网页——第九章 表单验证

    1.  表单验证:减轻服务器的压力.保证输入的数据符合要求: 2.  常用的表单验证:日期格式.表单元素是否为空.用户名和密码.E-mail地址.身份证号码等: 3.  表单验证的思路: 1.     ...

  8. 使用vue做表单验证

    <template> <Form ref="formInline" :model="formInline" :rules="rule ...

  9. element-ui做表单验证 v-for遍历表单 自动生成校验规则 pc移动双适配

    整体思路: 1:利用element-ui的栅格实现小分辨率和大分辨率的适配 2:模拟一组数据,从中筛选出 绑定各个表单值的对象   以及生成验证规则对象 3:在script标签内 .data()外,自 ...

随机推荐

  1. Arrays 类的 binarySearch() 数组查询方法详解

    Arrays类的binarySearch()方法,可以使用二分搜索法来搜索指定的数组,以获得指定对象.该方法返回要搜索元素的索引值.binarySearch()方法提供多种重载形式,用于满足各种类型数 ...

  2. 在django中进行MySQL入库

    在django中进行mysql 入库 需要导入 : from django.db import models   在添加主键时,需要使用:  primary_key=True id = models. ...

  3. WINDOWS系统的正确安装-硬盘格式如何选择

    有一种这样的说法,WIN7改装WIN10必须要重新分区,将硬盘格式化为GPT格式(GUID分区表 ), WIN10改装WIN7必须要重新分区,将硬盘格式化为MBR格式. 这种说法一直困扰着我,于是经过 ...

  4. hdu4336 Card Collector 容斥原理

    In your childhood, do you crazy for collecting the beautiful cards in the snacks? They said that, fo ...

  5. golang fatal error: concurrent map read and map write

    调试程序的时候,为了打印map中的内容 ,直接 使用seelog 的方法打印 map中的内容到日志,结果出现 “concurrent map read and map write”的错误,导致程序异常 ...

  6. VM安装系统时提示硬件不支持(unsupported hardware detected)

    修改一下虚拟机配置,把CD/DVD中使用的autoinst.iso替换成需要安装的系统镜像文件 然而,后面发现还是会报这个错,不过,却可以正常安装下去.但是如果不修改这里的话,安装系统时会出错

  7. 一不小心把win10的秘钥卸载了解决方法

    我遇到的第一个问题是Win10家庭版激活失败提示错误代码0xC004C003 然后我百度后看到一个解决方法是卸载秘钥然后再输入秘钥的,于是我执行了slmgr.vbs /upk,发现win10秘钥被卸载 ...

  8. skipper filter 扩展开发

    skipper 的扩展包含filter类型的,以及Predicates ,当然script(lua)脚本也是 这次主要是filter类型的开发 filter 接口约定 格式 filter 至少需要包含 ...

  9. c++内存泄漏原因及解决办法(智能指针)

    内存泄漏 由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费. 内存泄露的 ...

  10. mysql 的 help 命令:每个命令,都有相应的反斜杠(\)加一个字母或字符的简写

    mysql> help For information about MySQL products and services, visit: http://www.mysql.com/ For d ...