我有一个表单验证脚本,不幸的是在崩溃之前不久返回Stack overflow at line 0
警告框(IE7)并且在IE8中直接崩溃(它确实首先工作,非常慢)。
我为你的测试乐趣做了一个jsFiddle:http://jsfiddle.net/yuNXm/2/在你输入一个需要验证然后失去焦点的输入值之后发生堆栈溢出。 (电子邮件字段是ajax驱动的,因此不会在那里运行)。
相关的Javascript:
jQuery(document).ready(function($) {
var inputs = $('input[data-validation-method]');
var fields = $();
var classes = ['fail', 'win'];
//Methods of validation, must return an object like so {result: [boolean], message: [string or false]} as a parameter of the callback() function;
var methods = {
'email' : function(field, dependancies, callback) {
var value = field.val();
var response = false;
field.addClass("loading");
$.post(
ajaxData.url,
{
'action':'validate_form',
'value': value,
'method': field.data('method')
},
function(response){
return callback(response);
}
).complete(function() {
field.removeClass("loading");
});
},
'password' : function(field, dependancies, callback) {
var value = field.val();
var response = {};
if (value.length < 8) {
response.result = false;
response.message = 'Your password must be a minimum of 8 characters';
} else {
response.result = true;
response.message = false;
}
return callback(response);
},
'verify_password' : function(field, dependancies, callback) {
var value = field.val();
var response = {};
if (value != dependancies["password"].val()) {
if (!dependancies["password"].val() || !value) {
return false;
}
response.result = false;
response.message = 'Passwords do no match';
} else {
response.result = true;
response.message = false;
}
return callback(response);
}
}
// Prepare fields for validation
inputs.each(function() {
createField($(this));
});
function createField (field) {
inputs = inputs.not(field);
var method = field.attr('data-validation-method');
var requires = field.attr('data-validation-requires');
if (!!requires) {
requires = requires.split(',');
var dependancies = {};
$.each(requires, function(key, value) {
var element = $('#' + value);
if(element.length) {
dependancies[element.attr('id')] = element;
if(inputs.find(element).length) {
createField(element);
}
if ($.isArray(element.data('linked_fields'))) {
element.data('linked_fields').push(field);
} else {
element.data('linked_fields', [field]);
}
}
});
}
if (methods[method]) {
fields = fields.add('#' + field.attr('id'));
field.data('method', method);
field.data('dependancies', dependancies);
}
}
function validate (field) {
var callback = function(response) {
field.data('response', response);
if (response) {
toggleFlag(field, 'show');
} else {
toggleFlag(field, 'remove');
}
if($.isArray(field.data('linked_fields'))) {
$.each(field.data('linked_fields'), function(key, value) {
validate(value);
});
}
}
methods[field.data('method')](field, field.data('dependancies'), callback);
}
fields.focus(function() {
var field = $(this);
field.data("value", field.val());
field.bind("propertychange keyup input paste", function(event){
if(field.data("response") && (field.val() != field.data("value"))) {
toggleFlag(field, "hide");
if($.isArray(field.data('linked_fields'))) {
$.each(field.data('linked_fields'), function(key, value) {
toggleFlag(value, "hide");
});
}
}
});
});
fields.blur(function() {
var field = $(this);
if (field.val().length) {
if (field.val() != field.data("value")) {
toggleFlag(field, "remove");
validate(field);
} else {
toggleFlag(field, "show");
}
} else {
toggleFlag(field, "remove");
}
});
function toggleFlag (field, method) {
var flag = field.data("flag");
var response = field.data("response");
if (response) {
switch (method) {
case "show":
if (response.message) {
if(!flag) {
flag = $('<span class="pie ' + classes[~~response.result] + '">' + response.message + '</span>').insertAfter(field);
field.data("flag", flag);
flag.hide();
}
if (!flag.data("active")) {
flag.data("active", true);
flag.stop(true, true).animate({height: "show", opacity: "show"}, 500);
}
}
field.addClass(classes[~~response.result]);
break;
case "hide":
if (flag) {
if (flag.data("active")) {
flag.data("active", false);
flag.stop(true, true).animate({height: "hide", opacity: "hide"}, 500);
}
}
field.removeClass(classes[~~response.result]);
break;
case "remove":
if (flag) {
field.removeData("flag");
if (flag.data("active")) {
flag.stop(true, true).animate({height: "hide", opacity: "hide"}, 100, function() {
flag.remove();
});
}
}
field.removeClass(classes[~~response.result]);
field.removeData("response");
break;
}
}
}
});
相关的HTML:
<form action="" method="post" class="user-data">
<div class="fields">
<label for="email">Email:</label>
<input type="text" name="email" id="email" data-validation-method="email" class="text" value="" placeholder="youremail@somesite.com" />
<span class="info">We won\'t do anything cheeky with your email... promise.</span>
<label for="password">Choose a password:</label>
<input type="password" name="password" id="password" data-validation-method="password" class="text" value="" />
<label for="verify_password">Retype your password:</label>
<input type="password" name="verify_password" id="verify_password" class="text" data-validation-method="verify_password" data-validation-requires="password" value="" />
<input type="checkbox" name="mailing_list" value="true" /> <label for="mailing_list">I would like to recieve email updates about new features</label>
<span class="info">We won\'t spam your inbox, emails will be infrequent.</span>
</div>
<input type="submit" id="submitbtn" class="button omega" name="submit" value="Create your account" />
</form>
现在我知道这通常是由于递归,我在脚本的两个区域使用递归。
重复功能编号1:
function createField (field) {
inputs = inputs.not(field);
var method = field.attr('data-validation-method');
var requires = field.attr('data-validation-requires');
if (!!requires) {
requires = requires.split(',');
var dependancies = {};
$.each(requires, function(key, value) {
var element = $('#' + value);
if(element.length) {
dependancies[element.attr('id')] = element;
if(inputs.find(element).length) {
createField(element);
}
if ($.isArray(element.data('linked_fields'))) {
element.data('linked_fields').push(field);
} else {
element.data('linked_fields', [field]);
}
}
});
}
if (methods[method]) {
fields = fields.add('#' + field.attr('id'));
field.data('method', method);
field.data('dependancies', dependancies);
}
}
因为只有在与需要验证的输入进行交互时才会发生堆栈溢出,并且createField
函数仅用作初始化函数,我认为不是这个。
重复执行的功能编号2:
function validate (field) {
var callback = function(response) {
field.data('response', response);
if (response) {
toggleFlag(field, 'show');
} else {
toggleFlag(field, 'remove');
}
if($.isArray(field.data('linked_fields'))) {
$.each(field.data('linked_fields'), function(key, value) {
validate(value);
});
}
}
methods[field.data('method')](field, field.data('dependancies'), callback);
}
我无法访问任何其他外部程序来调试此(企业环境),有人能引导我朝着正确的方向前进吗?
答案 0 :(得分:12)
每当您使用jQuery propertychange
或addClass
时,Internet Explorer都会触发事件removeClass
。问题代码在这里:
var field = $(this);
field.data("value", field.val());
field.bind("propertychange keyup input paste", function(event){
if(field.data("response") && (field.val() != field.data("value"))) {
toggleFlag(field, "hide");
if($.isArray(field.data('linked_fields'))) {
$.each(field.data('linked_fields'), function(key, value) {
toggleFlag(value, "hide");
});
}
}
});
在toggleFlag
功能中,您可以调用jQuery的addClass
和removeClass
。这造成了无限递归循环,导致堆栈溢出。
如果您取出propertychange
,它在Internet Explorer以及所有其他浏览器上运行良好。
工作示例: http://jsfiddle.net/yuNXm/9/
仅在Internet Explorer上出现此问题的原因是onpropertychange是Microsoft为Internet Explorer实施的专有事件。它没有被其他浏览器实现。
使用IE6-8调试堆栈溢出:
将来可以用来诊断这些类型的堆栈溢出的好方法是:
确定无限递归循环所涉及的一个函数。如果您在没有调试功能的情况下坚持使用IE6-8,那么这需要在各种功能中发出警报,直到您找到无限循环功能。
将此行代码放在函数顶部:
alert(arguments.callee.caller.toString());
此警报将告诉您哪个函数正在调用无限循环函数。然后通过回溯无限递归循环中涉及哪些函数,您可以隔离代码中需要仔细检查无限循环原因的部分。
当然,如果你有一个带有适当调试工具的现代网络浏览器,这不是必需的 - 你只需要逐步完成代码。
另一方面,我感受到了你的痛苦。我的工作的一部分还涉及为企业客户编写JavaScript,其中IE6-8通常是他们的IT部门强加的浏览器。没有调试工具,只有警报和评论;在对堆栈溢出进行故障排除时,甚至不能使用行号。