2012-07-19 62 views
8

我有一个表单验证脚本,不幸的是在崩溃之前(IE7)不久返回了Stack overflow at line 0警报框,并在IE8中直接崩溃(它首先工作,非常缓慢)。堆栈在第0行溢出

我已经为您的测试乐趣制作了一个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="[email protected]" /> 
    <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); 
} 

我没有访问任何其他外部程序调试这个(公司环境),任何人都可以导致我在这里向正确的方向?

+2

您的企业环境不会让您获得工作所需的工具吗? – 2012-07-19 13:32:56

+0

@Michael一旦我们的企业环境经过测试,我的企业环境将允许我在工作后的几个月内完成工作,并且我的请求已通过多个(非技术性)团队完成。这将是无关紧要的。 – 2012-07-19 13:50:12

+13

有了这样的故事,我认为[this](http://careers.stackoverflow.com/)是我可以给你的最好的链接。 – SomeKittens 2012-07-19 15:35:58

回答

12

无论何时使用jQuery到addClassremoveClass,Internet Explorer都会触发事件propertychange。问题代码是在这里:

 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的addClassremoveClass。这就造成了导致堆栈溢出的无限递归循环。

如果您拿出propertychange,它在Internet Explorer以及所有其他浏览器上效果很好。

工作例如:http://jsfiddle.net/yuNXm/9/

你只能在Internet Explorer遇到此问题的原因是,onpropertychange是微软的Internet Explorer实现私有事件。它没有被其他浏览器实现。

调试堆栈溢出与IE6-8:

你可以在将来用它来诊断这些类型的堆栈溢出的一个好方法是:

  1. 确定所涉及的功能之一与无限递归循环。如果你在IE6-8中没有调试功能,那么这需要在各种函数中放置警报,直到找到一个无限循环的函数。

  2. 广场这行代码在函数的顶部:

    alert(arguments.callee.caller.toString());

此警报会告诉你哪个函数调用无限循环的功能。然后通过追溯无限递归循环中涉及哪些函数,可以隔离代码中需要仔细检查无限循环原因的部分。

当然,如果你有一个现代化的网络浏览器和适当的调试工具,这是没有必要的 - 你只需要通过代码。

在旁注中,我感到你的痛苦。我的部分工作还涉及为公司客户编写JavaScript,其中IE6-8通常是其IT部门强加的浏览器。无需调试工具,只需提醒和评论;甚至在解决堆栈溢出问题时都不需要使用行号。

+0

您是否尝试过Microsoft Script Editor?我听说它(在某些方面)比IE9/10 devtools更好。特别是如果你只需要测试某个部分,你可以拖动执行指针。 – 2012-08-08 02:27:09

+2

我希望,但我的电脑不是问题(我可以安装本地需要的任何调试工具),这是我必须通过Citrix(或现场)远程登录到企业客户端系统以诊断某种问题这是特定于他们的安装。这些计算机通常被IT服务锁定,并且安装脚本编辑器或其他通常不可行的选项。 – 2012-08-08 03:45:37

+1

您值得拥有一枚金牌,我梦想拥有现代化的网页浏览器,并且非常高兴我不是唯一一个。我永远不会想到这件事,非常感谢您提供的深入分析和提示。 – 2012-08-09 12:29:22