所以我构建了这个插件,在测试它时一切运行良好,直到我最终到达插件的onSubmit
部分。发生的事情是表单会成功提交一次,但之后它会提交的次数增加一倍。我不知道这是怎么回事,因此我转向你们寻求帮助。
我提供了一个演示表单,插件使用表单和插件本身进行操作的方式。由于我不确定它在插件中的来源,因此我提供了完整的源代码。如果还有其他任何需要帮助,请告诉我。
HTML
<form id='test'>
<div class='form-group'>
<input type='text' id='data' class='form-control' />
</div>
<button type='submit' class='btn btn-default'>Submit</button>
</form>
的javascript
$(function(){
$('#test').validator({
controls:{
data : {
validate : 'notEmpty'
}
},
onSubmit : function(){
alert("success");
// destroy the instance
$('#test').validator('destroy');
// get the original options
var options = $('#test').validator('getSettings');
// re initialize the plugin for the form
$('#test').validator(options);
}
});
});
validator.js
(function($){
var _defaults = null;
// functions object where the validator functions are stored for use
var functions = {
notEmpty : function(value){
return value && $.trim(value).length > 0;
},
required : function(value){
return value && $.trim(value).length > 0;
},
isEmail : function(value){
var check = true;
if(value.length)
check = /^([^@\s\t\n]+\@[\w\d]+\.[\w]{2,3}(\.[\w]{2})?)$/.test(value);
return check;
},
isPhoneNumber : function(value){
var check = true;
if(value.length)
check = /^(\d\-)?\(?\d{3}\)?[\-|\s]?\d{3}[\-|\s]?\d{4}$/.test(value);
return check;
},
isNumber : function(value){
var check = true;
if(value.length)
check = /^[+-]?\d+(\.\d+)?$/.test(value);
return check;
},
isSSN : function(value){
var check = true;
if(value.length)
check = /^\d{3}\-?\d{2}\-?\d{4}$/.test(value);
return check;
},
isString : function(value){
var check = true;
if(value.length)
check = /^\D+$/.test(value);
return check;
},
isURL : function(value){
var check = true;
if(value.length)
check = /^([http\:\/\/]+)?([a-zA-Z]+)?\.?[a-zA-Z0-9\-]+\.[a-zA-Z]+$/.test(value);
return check;
},
isDateTime : function(value, regex){
var check = true;
if(regex == null){
return true;
}
var regexChars = regex.split("");
var pattern = "^";
for(var i = 0; i < regexChars.length; i++){
switch(regexChars[i]){
case "d" :
case "j" :
pattern = pattern + "(0?[1-9]|[12][0-9]|3[01])";
break;
case "D":
pattern = pattern + "(Sun|Mon|Tue|Wed|Thu|Fri|Sat)";
case "l" :
pattern = pattern + "(Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)";
break;
case "F" :
pattern = pattern + "(January|February|March|April|May|June|July|August|September|October|November|December)";
break;
case "M" :
pattern = pattern + "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
break;
case "m" :
case "n" :
pattern = pattern + "(0?[1-9]|1[012])";
break;
case "Y" :
case "y" :
pattern = pattern + "(19|20)?[\\d]+";
break;
case "a" :
case "A" :
pattern = pattern + "([AaPp][Mm])";
break;
case "g" :
case "G" :
case "h" :
case "H" :
pattern = pattern + "(0?[0-9]|1[012]|2[0123])";
break;
case "i" :
case "s" :
pattern = pattern + "([012345][0-9])";
break;
case "/" :
pattern = pattern + "[/]";
break;
case ":" :
pattern = pattern + "[:]";
break;
case "." :
pattern = pattern + "[.]";
break;
case " " :
pattern = pattern + "[ ]";
break;
case "," :
pattern = pattern + "[,]";
break;
case "-" :
pattern = pattern + "[-]";
break;
}
}
pattern = pattern+"$";
pattern = new RegExp(pattern, 'i');
if(value.length)
check = pattern.test(value);
return check;
},
groupNotEmpty : function(value){
return value.length > 0;
},
isRoutingNumber : function(value){
//run through each digit and calculate the total
var n = 0;
for(var i = 0; i < value.length; i += 3){
n += parseInt(value.charAt(i), 10)*3 + parseInt(value.charAt(i+1), 10)*7 + parseInt(value.charAt(i+2), 10);
}
//if the resulting sum is an even multiple of ten (but not zero), the aba routing number is good
if(n != 0 && n % 10 == 0){
return true;
}else{
return false;
}
},
isMacAddress : function(value){
var check = true;
if(value.length){
check = /^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$/.test(value);
}
return check;
},
isIPAddress : function(value){
var check = true;
if(value.length){
check = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(value);
}
return check;
},
validPassword : function(value, selector){
var form = this.$form;
var that = this;
var check = false;
var strength = 0;
if(value.length >= 1){
strength = 4;
}
// password mixins
var passwordChars = value.split('');
var count = 0; // keep track of the characters
for(p in passwordChars){
count++;
if(count < 8){
strength += 5;
}else if(count >= 8 && count < 20){
strength += 3;
}else if(count >= 20){
strength += 2;
}
if(/[0-9]/.test(passwordChars[p])){
strength += 3;
}
if(/[a-z]/.test(passwordChars[p])){
strength += 1;
}
if(/[A-Z]/.test(passwordChars[p])){
strength += 2;
}
if(/[!$@#]/.test(passwordChars[p])){
strength += 4;
}
}
var progressBarHolder = $(form).find(selector).closest('.form-group');
var progressBar = $(progressBarHolder).find('.progress-bar');
var progressBarValue = strength;
if(strength < 50){
$(progressBar).css({
'width' : progressBarValue+'%',
'background-color' : '#D9534F'
});
$(progressBar).html(progressBarValue+'%');
}else if(strength >= 50 && strength < 80){
$(progressBar).css({
'width' : progressBarValue+'%',
'background-color' : '#F0AD4E'
});
$(progressBar).html(progressBarValue+'%');
}else if(strength >= 80 && strength <= 100){
$(progressBar).css({
'width' : progressBarValue+'%',
'background-color' : '#5CB85C'
});
$(progressBar).html(progressBarValue+'%');
}else if(strength > 100){
$(progressBar).css({
'width' : '100%',
'background-color' : '#5CB85C'
});
$(progressBar).html('100%');
}
if(strength >= 6){
check = true;
}
return check;
},
equalTo : function(value, selector){
var form = this.$form;
var check = false;
var checkValue = $(form).find(selector).val();
if(value.length){
if(value === checkValue){
check = true;
}
}
return check;
},
isCreditCard : function(value){
var that = this;
var check = false;
var value = value.replace(/[ -]/g, '');
var card_types = [
{
name : 'amex',
pattern: /^3[47]/,
valid_length: [15]
}, {
//diners_club_carte_blanche
name: 'dccb',
pattern: /^30[0-5]/,
valid_length: [14]
}, {
//diners_club_international
name: 'dci',
pattern: /^36/,
valid_length: [14]
}, {
name: 'jcb',
pattern: /^35(2[89]|[3-8][0-9])/,
valid_length: [16]
}, {
name: 'laser',
pattern: /^(6304|670[69]|6771)/,
valid_length: [16, 17, 18, 19]
}, {
//visa_electron
name: 'electron',
pattern: /^(4026|417500|4508|4844|491(3|7))/,
valid_length: [16]
}, {
name: 'visa',
pattern: /^4/,
valid_length: [16]
}, {
//mastercard
name: 'mc',
pattern: /^5[1-5]/,
valid_length: [16]
}, {
name: 'maestro',
pattern: /^(5018|5020|5038|6304|6759|676[1-3])/,
valid_length: [12, 13, 14, 15, 16, 17, 18, 19]
}, {
name: 'discover',
pattern: /^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/,
valid_length: [16]
}
];
if(value.length){
var exists = false;
var matches = false;
var validlength = false;
var validLuhn = false;
var cardname = "";
that.setCreditCardImage(cardname);
for(var name in card_types){
if(value.match(card_types[name].pattern)){
matches = true;
}
if(card_types[name].valid_length.indexOf(value.length) > -1){
validlength = true;
}
if(matches && validlength){
cardname = card_types[name].name.toLowerCase();
break;
}
}
if(matches && validlength){
var digit, n, sum, j, len, ref1;
sum = 0;
ref1 = value.split('').reverse();
for(n = j = 0, len = ref1.length; j < len; n = ++j){
digit = ref1[n];
digit = +digit;
if(n % 2){
digit *= 2;
if(digit < 10){
sum += digit;
}else{
sum += digit - 9;
}
}else{
sum += digit;
}
}
if(sum % 10 === 0){
validLuhn = true;
}
}else{
return check;
}
if(matches && validlength && validLuhn){
check = true;
that.setCreditCardImage(cardname);
return check;
}else{
that.setCreditCardImage();
return check;
}
}else{
check = true;
return check;
that.setCreditCardImage();
}
},
isZipCode : function(value){
var check = true;
if(value.length){
check = /^\d{5}(?:[-\s]\d{4})?$/.test(value);
}
return check;
},
isDependent : function(value, selector){
var check = true;
if(selector.length){
if(selector.substring(0,1) === '_'){
selector = "[data-validator*=isDependent"+selector+"]";
}
if(!this.notEmpty(value)){
form.find(selector).each(function(){
if($(this).val() !== ''){
check = false;
}
});
}
}
return check;
},
testRegex : function(value, regex){
var check = true;
var caret = regex.substring(0);
var dollar = regex.substring(regex.length-1);
if(caret != "^")
regex = "^"+regex;
if(dollar != "$")
regex = regex+'$';
if(value.length && regex.length){
var pattern = new RegExp(regex, 'i');
check = pattern.test(value);
}
return check;
}
};
var selectors = [];
// private function that sets the form control data-validator values
var setControlData = function(selector, options){
var validate = options.validate;
var validations = "";
if(Array.isArray(validate)){
validations = validate.join("|");
}else{
validations = validate;
}
if(validations.indexOf('isDateTime') > -1 && typeof(options.dateFormat) != 'undefined' && typeof(options.dateFormat) == 'string'){
validations = validations.replace(/isDateTime/g, 'isDateTime'+options.dateFormat);
}
if(validations.indexOf('equalTo') > -1 && typeof(options.equalTo) != 'undefined' && typeof(options.equalTo) == 'string'){
validations = validations.replace(/equalTo/g, 'equalTo'+options.equalTo);
}
if(validations.indexOf('testRegex') > -1 && typeof(options.regex) != 'undefined' && typeof(options.regex) == 'string'){
validations = validations.replace(/testRegex/g, 'testRegex'+options.regex);
}
$(selector).data('validator', validations);
};
// main function that validates the form itself
var validator = function(selector){
var rVal = true;
var element = selector;
var errors = 0;
//check if there is a validator option for the specific input and that it has a value to it
if($(element).data('validator')){
// get the element type and split the pipe separated values into an array
var value = null;
var type = $(element).attr('type');
var formGroup = null;
if(type != "radio" && type != "checkbox"){
formGroup = $(element).closest('.form-group');
value = $(element).val();
}else{
formGroup = $(element).closest('.form-group');
value = [];
$(formGroup).find('.radio, .checkbox, .radio-inline, .checkbox-inline').each(function(){
$(":checked", this).each(function(){
value.push($(this).val());
});
});
}
// check the .data('validator') string to account for interior pipes that may be used with regexes or selectors
var validations = $(element).data('validator');
validations = validations.replace(/\|\=/g, '%=');
var holding = [];
if(validations.indexOf('testRegex') > -1){
var hold = validations.substring(validations.indexOf('testRegex'), validations.lastIndexOf('/')+1);
holding.push(hold);
validations = validations.replace(hold, '');
}
validations = validations.replace("||", "|");
validations = validations.split('|');
validations = validations.concat(holding);
for(var x in validations){
validations[x] = validations[x].replace('%=', '|=');
}
validations = validations.filter(function(n){return n != undefined && n != ""});
for(var i = 0; i < validations.length; i++){
if(validations[i].indexOf('validPassword') !== -1){
var selector = validations[i].replace('validPassword', '');
!functions['validPassword'](value, selector) ? errors++ : null;
}else if(validations[i].indexOf('isDateTime') !== -1){
var regex = validations[i].replace('isDateTime', '');
!functions['isDateTime'](value, regex) ? errors++ : null;
}else if(validations[i].indexOf('equalTo') !== -1){
var selector = validations[i].replace('equalTo', '');
!functions['equalTo'](value, selector) ? errors++ : null;
}else if(validations[i].indexOf('isDependent') !== -1){
var selector = validations[i].replace('isDependent', '');
!functions['isDependent'](value, selector) ? errors++ : null;
}else if(validations[i].indexOf('testRegex') !== -1){
var regex = validations[i].replace('testRegex', '');
var regex = regex.substring(regex.indexOf('/')+1, regex.lastIndexOf('/'));
!functions['testRegex'](value, regex) ? errors++ : null;
}else{
!functions[validations[i]](value) ? errors++ : null;
}
}
if(errors > 0){
$(formGroup).addClass('has-error');
}else{
$(formGroup).removeClass('has-error');
}
if(errors > 0) rVal = false;
}else{
formGroup = $(element).closest('.form-group');
if(errors > 0){
$(formGroup).addClass('has-error');
}else{
$(formGroup).removeClass('has-error');
}
}
return rVal;
};
// object literal for the methods
var methods = {
// defining the individual methods inside of the literal
init : function(options){
// Repeat this process over each element in the selector
return this.each(function(){
var $this = $(this);
var settings = options;
_defaults = options;
console.log(settings);
// validation to make sure at least the bare minimum is supplied
if(typeof(settings) == 'undefined'){
console.log("No options were supplied for this plugin. 'selectors' and 'onSubmit' are required. Please see documentation for assistance");
return false;
}else if(typeof(settings.controls) != 'object'){
console.log("'selectors' must be an object");
return false;
}else if(typeof(settings.onSubmit) != 'function'){
console.log("'onSubmit' is a callback function");
return false;
}
// if nothing else was wrong proceed to run the rest of the program here
var controls = settings.controls;
var bindInput = typeof settings.bindInput != 'undefined' && settings.bindInput != false ? true : false;
var showErrorMessage = typeof settings.showErrorMessage != 'undefined' && settings.showErrorMessage != false ? true : false;
for(var control in controls){
if(typeof controls[control] != 'object'){
console.log("controls."+control+" must be an object. Skipping this element");
continue;
}else if(typeof controls[control].validate == 'undefined'){
console.log("controls."+control+".validate must be defined");
continue;
}
$($this).find('input, textarea, select').each(function(){
if($(this).is('#'+control)){
setControlData('#'+control, controls[control]);
selectors.push('#'+control);
}else if($(this).hasClass(control)){
setControlData('.'+control, controls[control]);
selectors.push('.'+control);
}
});
}
if(bindInput){
$($this).find('input, textarea, select').on('change keyup click select focus', function(){
for(var s in selectors){
validator(selectors[s]);
}
});
}
if(showErrorMessage){
$($this).prepend("<section id='errorAlert' class='form-group col-xs-12 hidden'><div class='alert alert-danger' ></div></section>");
}
// handle the form submission
$($this).submit(function(e){
e.preventDefault();
var errors = "";
var rVal = true;
for(selector in selectors){
if(!validator(selectors[selector])){
rVal = false;
if(showErrorMessage){
var pos = selectors.indexOf(selector);
var control = selectors[pos].replace(/[.#]/g, '');
if(typeof controls[control].errorMessage != "undefined" && typeof controls[control].errorMessage == 'string'){
errors += "• "+controls[control].errorMessage+"<br>";
}
}
}
}
if(rVal){
if(showErrorMessage){
$('#errorAlert').addClass('hidden');
}
settings.onSubmit();
}else{
if(showErrorMessage){
$('#errorAlert').removeClass('hidden');
$('#errorAlert > div').html(errors);
$('html, body').animate({
scrollTop : $('#errorAlert').offset().top
}, 1000);
}
}
});
});
},
checkControl : function(control, options){
return this.each(function(){
setControlData(control, options);
validator(control);
$(control).on('change keyup click select focus', function(){
validator(control);
});
});
},
clearControl : function(control){
return this.each(function(){
$(control).removeData('validator');
$(control).off('change keyup click select focus');
validator(control);
});
},
getSettings : function(){
return _defaults;
},
destroy : function(){
return this.each(function(){
$(this).find('input, select, textarea').each(function(){
$(this).data('validator', '');
validator($(this));
$(this).removeData('validator');
});
});
}
}
// the engine
$.fn.validator = function(){
// get the first argument passed if any
var method = arguments[0];
if(methods[method]){
method = methods[method];
arguments = Array.prototype.slice.call(arguments, 1);
// if there was no argument or the argument is the default argument object then proceed to initialize
}else if(typeof(method) == 'object' || !method){
method = methods.init;
}else {
console.log("Method "+ method + " does not exist for this plugin");
return this;
}
// return the result that is wanted based on what was passed in the initial arguments
return method.apply(this, arguments);
}
})(jQuery);
答案 0 :(得分:1)
通过每次销毁并重新初始化插件,您依赖插件正确解除所有事件处理程序的绑定,在这种情况下它似乎不会。
可能更容易为插件提供一个方法来重置输入上的数据和值,从而为您提供一个新的表单,而无需重新初始化。