8

残念ながら、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>

これは通常、再帰が原因であることがわかりました。スクリプトの2つの領域で再帰を使用しています。

定期的な機能番号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);
}

私はこれをデバッグするための他の外部プログラム(企業環境)にアクセスできませんが、誰かが私をここで正しい方向に導くことができますか?

4

1 に答える 1

12

Internet Explorerは、 jQueryをまたはpropertychangeに使用するたびにイベントを発生させます。問題のコードはここにあります:addClassremoveClass

     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関数では、jQueryaddClassとを呼び出しますremoveClass。これにより、スタックオーバーフローにつながる無限再帰ループが作成されました。

取り出すと、propertychangeInternetExplorerだけでなく他のすべてのブラウザでもうまく機能します。

実例: http: //jsfiddle.net/yuNXm/9/

Internet Explorerでのみこの問題が発生した理由は、onpropertychangeがMicrosoftがInternetExplorer用に実装した独自のイベントであるためです。他のブラウザでは実装されていません。

IE6-8でのデバッグスタックオーバーフロー:

これらのタイプのスタックオーバーフローを診断するために将来使用できる良い方法は、次のとおりです。

  1. 無限再帰ループに関連する関数の1つを特定します。デバッグ機能のないIE6-8で立ち往生している場合は、無限にループする関数が見つかるまで、さまざまな関数にアラートを配置する必要があります。

  2. 次のコード行を関数の先頭に配置します。

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

このアラートは、どの関数が無限ループ関数を呼び出しているかを示します。次に、どの関数が無限ループに関与しているかをトレースバックすることで、無限ループの原因を綿密に調べる必要があるコードの部分を分離できます。

もちろん、適切なデバッグツールを備えた最新のWebブラウザーを使用している場合、これは必要ありません。コードをステップ実行するだけです。

ちなみに、私はあなたの痛みを感じます。私の仕事の一部には、IE6-8がIT部門によって課せられるブラウザであることが多い企業クライアント向けのJavaScriptのコーディングも含まれます。デバッグツールはなく、アラートとコメントだけです。スタックオーバーフローのトラブルシューティングを行うときに使用する行番号すらありません。

于 2012-08-08T00:33:29.817 に答える