2012-07-19 18 views
8

Tengo un script de validación de formulario que desafortunadamente devuelve el cuadro de alerta Stack overflow at line 0 poco antes de bloquearse (IE7) y simplemente se bloquea directamente en IE8 (Funciona primero, muy lentamente).Desbordamiento de pila en la línea 0

He creado un jsFiddle para su placer de prueba: http://jsfiddle.net/yuNXm/2/ el desbordamiento de la pila se produce después de haber ingresado un valor en una entrada que requiere validación y luego perder su foco. (el campo de correo electrónico es ajax, por lo que no funcionará allí).

El Javascript relevante:

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; 
      } 
     } 
    } 

}); 

El HTML relevante:

<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> 

Ahora sé que esto se debe normalmente a la recursividad, y usar recursividad en dos áreas de la secuencia de comandos.

número de funcionamiento recurrente 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); 
    } 
} 

Debido a que el desbordamiento de pila se produce sólo cuando se interactúa con una entrada que necesita validación, y la función createField sólo se utiliza como una función de inicialización no creo es este.

de funcionamiento recurrente número 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); 
} 

que no tienen acceso a otros programas externos para depurar este (entorno corporativo), ¿alguien puede conducir en la dirección correcta aquí?

+2

¿Su entorno corporativo no le permitirá las herramientas que necesita para hacer su trabajo? –

+0

@Michael Mi entorno corporativo me permitirá las herramientas que necesito para hacer mi trabajo meses después, una vez que hayan sido probados y mi solicitud se haya abierto camino a través de la multitud de equipos (no técnicos) involucrados. Esto será irrelevante para entonces. –

+13

Con una historia como esa, creo que [este] (http://careers.stackoverflow.com/) es el mejor enlace que puedo darte. – SomeKittens

Respuesta

12

Internet Explorer está activando el evento propertychange cada vez que utiliza jQuery en addClass o removeClass. El código de problema está aquí:

 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"); 
        }); 
       } 
      } 
     }); 

En su función toggleFlag que llaman addClass y removeClass de jQuery. Eso creó el bucle infinito de recursión que condujo al desbordamiento de la pila.

Si saca el propertychange funciona muy bien en Internet Explorer, así como en todos los demás navegadores.

Ejemplo de trabajo:http://jsfiddle.net/yuNXm/9/

La razón por la que estaba teniendo este problema sólo en Internet Explorer es que onpropertychange es un evento patentada implementada por Microsoft para Internet Explorer. No es implementado por otros navegadores.

pila de depuración se desborda con IE6-8:

Un buen método que puede utilizar en el futuro para el diagnóstico de este tipo de desbordamientos de pila es:

  1. Identificar una de las funciones involucradas con el ciclo de recursión infinito.Si está atascado con IE6-8 sin capacidad de depuración, esto implica colocar alertas en varias funciones hasta que encuentre una función de bucle infinito.

  2. Coloque esta línea de código en la parte superior de la función:

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

Esta alerta le dirá qué función está llamando a la función infinitamente bucle. Luego, rastreando qué funciones están involucradas en el ciclo de recursión infinito, puedes aislar las partes de tu código que necesitas para examinar de cerca la causa del ciclo infinito.

Por supuesto, si tiene un navegador web moderno con las herramientas de depuración adecuadas, esto no es necesario; simplemente pasará por el código.

En una nota lateral, siento tu dolor. Parte de mi trabajo también involucra la codificación de JavaScript para clientes corporativos donde IE6-8 es a menudo el navegador impuesto por su departamento de TI. No hay herramientas de depuración, solo alertas y comentarios; ni siquiera un número de línea para trabajar cuando estás solucionando problemas de desbordamientos de pila.

+0

¿Has probado Microsoft Script Editor? Escuché que es (de alguna manera) mejor que el IE9/10 devtools. En particular, puede arrastrar el puntero de ejecución si solo necesita probar una determinada parte. –

+2

Ojalá, pero mi computadora no es el problema (puedo instalar cualquier herramienta de depuración que desee localmente), es cuando tengo que iniciar sesión en un sistema de cliente corporativo remotamente a través de Citrix (o en el sitio) para diagnosticar algún tipo de problema eso es específico para su instalación. En general, estos equipos están bloqueados por los Servicios de TI e instalar el Editor de scripts o algo más generalmente no es una opción. –

+1

Merece una medalla de oro, sueño con los navegadores web modernos y me alegro de no ser el único. Nunca lo hubiera pensado y estoy muy agradecido por el análisis en profundidad y los consejos que me brindó. –

Cuestiones relacionadas