2012-07-19 20 views
8

Mam skrypt sprawdzania poprawności formularza, który niestety zwraca błąd w polu alertu Stack overflow at line 0 na krótko przed awarią (IE7) i po prostu wypada w górę w IE8 (najpierw działa bardzo wolno).Przepełnienie stosu w linii 0

Zrobiłem jsFiddle dla twojej przyjemności testowania: http://jsfiddle.net/yuNXm/2/ przepełnienie stosu występuje po wprowadzeniu wartości do danych wejściowych, które wymagają sprawdzenia, a następnie utracą fokus. (pole email jest obsługiwane przez ajax, więc nie będzie tam działać).

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

}); 

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

Teraz wiem, że to zwykle z powodu rekursji i używam rekursji w dwóch obszarach skryptu.

cykliczne numer funkcji 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); 
    } 
} 

Ponieważ przepełnienie stosu występuje tylko podczas interakcji z wejściem, który potrzebuje sprawdzania poprawności, a funkcja createField służy jedynie jako funkcja inicjalizacji nie sądzę to jest ten.

cykliczne numer funkcji 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); 
} 

nie mam dostępu do żadnych innych programów zewnętrznych do debugowania to (środowisko korporacyjne), może ktoś prowadzi mnie w kierunku prawego tutaj?

+2

Twoje środowisko korporacyjne nie pozwoli Ci na narzędzia potrzebne do wykonywania pracy? –

+0

@Michael Moje środowisko korporacyjne zapewni mi narzędzia, których potrzebuję, aby wykonywać swoją pracę przez wiele miesięcy po przetestowaniu i moja prośba dotarła do wielu różnych (nietechnicznych) zespołów. Do tego czasu nie będzie to miało znaczenia. –

+13

Z taką historią uważam, że [this] (http://careers.stackoverflow.com/) to najlepszy link, jaki mogę ci dać. – SomeKittens

Odpowiedz

12

Program Internet Explorer uruchamia zdarzenie propertychange za każdym razem, gdy używa się jQuery do addClass lub removeClass. Kod problemem jest tutaj:

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

W funkcji toggleFlag zadzwonić jQuery addClass i removeClass. To stworzyło nieskończoną pętlę rekursji, która doprowadziła do przepełnienia stosu.

Jeśli wyłączysz propertychange, działa doskonale w Internet Explorerze, a także we wszystkich innych przeglądarkach.

przykład robocza:http://jsfiddle.net/yuNXm/9/

Powodem miałeś ten problem tylko w przeglądarce Internet Explorer jest to, że onpropertychange jest zastrzeżonym wydarzenie realizowane przez Microsoft Internet Explorer. Nie jest implementowany przez inne przeglądarki.

Debugowanie stos przepełnia IE6-8:

Dobrą metodą można wykorzystać w przyszłości do diagnozowania tego typu przepełnienia stosu jest:

  1. zidentyfikować jedną z funkcji zaangażowanych z nieskończoną pętlą rekursji.Jeśli utkniesz w IE6-8 bez możliwości debugowania, oznacza to umieszczanie alertów w różnych funkcjach, aż znajdziesz nieskończoną funkcję zapętlenia.

  2. Place ta linia kodu na górze funkcji:

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

Ten alert was, która funkcja jest wywołanie funkcji nieskończenie pętli powiedzieć. Następnie, śledząc, które funkcje są zaangażowane w nieskończoną pętlę rekursji, można wyizolować części kodu, które należy dokładnie zbadać pod kątem przyczyny nieskończonej pętli.

Oczywiście, jeśli masz nowoczesną przeglądarkę internetową z odpowiednimi narzędziami do debugowania, nie jest to konieczne - wystarczy przejrzeć kod.

Na marginesie, czuję twój ból. Część mojej pracy obejmuje również kodowanie JavaScript dla klientów korporacyjnych, gdzie IE6-8 jest często przeglądarką narzuconą przez dział IT. Brak narzędzi do debugowania, tylko ostrzeżenia i komentarze; nie ma nawet numeru linii, z którym można pracować przy rozwiązywaniu problemów z przepełnieniem stosu.

+0

Czy próbowałeś Microsoft Script Editor? Słyszałem, że jest (w pewnym sensie) lepszy niż devtools IE9/10. W szczególności możesz przeciągnąć wskaźnik wykonawczy, jeśli potrzebujesz tylko przetestować określoną część. –

+2

Życzę, ale mój komputer nie jest problemem (mogę zainstalować narzędzia debugowania, które chcę lokalnie), kiedy muszę zalogować się do systemu klienta korporacyjnego zdalnie przez Citrix (lub na miejscu), aby zdiagnozować jakiś problem to jest specyficzne dla ich instalacji. Te komputery są zazwyczaj blokowane przez Usługi IT i instalowanie Edytora skryptów lub coś innego, na ogół nie jest opcją. –

+1

Zasługujesz na złoty medal, marzę o nowoczesnych przeglądarkach internetowych i cieszę się, że nie jestem jedyny. Nigdy bym o tym nie pomyślał i jestem bardzo wdzięczny za dogłębną analizę i wskazówki, które mi przekazałeś. –

Powiązane problemy