web-dev-qa-db-ja.com

javascriptを介してフォームの変更を追跡する最良の方法は何ですか?

Javascriptを介してフォームの入力の変更を追跡したいと思います。私の意図は(しかし限定されない)

  • 何かが変更された場合にのみ「保存」ボタンを有効にします
  • ユーザーがページを閉じたいが、何かが保存されていない場合に警告する

アイデア?

45
Ricardo Acras

すべての入力要素をループし、それぞれにonchangeハンドラーを配置します。それが起動したら、フォームが変更されたことを知らせるフラグを設定します。その基本バージョンはセットアップが非常に簡単ですが、誰かが入力を「a」から「b」に変更してから「a」に戻したかどうかを認識するほど賢くはありません。そのケースをキャッチすることが重要である場合、それはまだ可能ですが、もう少し作業が必要になります。

JQueryの基本的な例を次に示します。

$("#myForm")
    .on("input", function() {
        // do whatever you need to do when something's changed.
        // perhaps set up an onExit function on the window
        $('#saveButton').show();
    })
;
32
nickf

JSのテキストフォーム要素は_.value_プロパティと_.defaultValue_プロパティを公開するため、次のようなものを簡単に実装できます。

_function formChanged(form) {
  for (var i = 0; i < form.elements.length; i++) {
      if(form.elements[i].value != form.elements[i].defaultValue) return(true);
  }
  return(false);
}
_

チェックボックスとラジオボタンについては、_element.checked != element.defaultChecked_かどうかを確認し、HTMLの_<select />_要素については、_select.options_配列をループして、_selected == defaultSelected_かどうか各オプションを確認する必要があります。

jQuery のようなフレームワークを使用して、個々のフォーム要素のonchangeイベントにハンドラーをアタッチすることを検討することをお勧めします。これらのハンドラーは、formChanged()コードを呼び出して、[保存]ボタンのenabledプロパティを変更したり、ドキュメント本体のbeforeunloadイベントのイベントハンドラーをアタッチ/デタッチしたりできます。 。

17
Dylan Beattie

試して

function isModifiedForm(form){
  var __clone = $(form).clone();
  __clone[0].reset();
  return $(form).serialize() == $(__clone).serialize();
}

その助けを願っています))

7
Nikita Makarov

フォームの変更を検出するための簡単なjavascriptとjqueryのメソッドを次に示します。変更が行われるまで送信ボタンを無効にします。フォームを送信する以外の方法でページを離れる試みを検出します。ユーザーによる「取り消し」を説明し、アプリケーション内で簡単に機能するようにカプセル化されており、送信時に失敗することはありません。関数を呼び出して、フォームのIDを渡すだけです。

この関数は、ページが読み込まれたときに一度、ユーザーがページを離れる前にフォームをシリアル化します。 2つのフォームの状態が異なる場合、プロンプトが表示されます。

試してみてください: http://jsfiddle.net/skibulk/ev5rE/

function formUnloadPrompt(formSelector) {
    var formA = $(formSelector).serialize(), formB, formSubmit = false;

    // Detect Form Submit
    $(formSelector).submit( function(){
        formSubmit = true;
    });

    // Handle Form Unload    
    window.onbeforeunload = function(){
        if (formSubmit) return;
        formB = $(formSelector).serialize();
        if (formA != formB) return "Your changes have not been saved.";
    };

    // Enable & Disable Submit Button
    var formToggleSubmit = function(){
        formB = $(formSelector).serialize();
        $(formSelector+' [type="submit"]').attr( "disabled", formA == formB);
    };

    formToggleSubmit();
    $(formSelector).change(formToggleSubmit);
    $(formSelector).keyup(formToggleSubmit);
}



// Call function on DOM Ready:

$(function(){
    formUnloadPrompt('form');
});
4
skibulk

Ars Technicaでこのような質問に回答しましたが、ユーザーがテキストフィールドをぼかしていない場合でも変更を検出する必要があるように質問を組み立てました(この場合、変更イベントは発生しません)。私は包括的なスクリプトを思い付きました:

  1. フィールド値が変更された場合に送信ボタンとリセットボタンを有効にします
  2. フォームがリセットされた場合、送信ボタンとリセットボタンを無効にします
  3. フォームデータが変更され、送信されていない場合、ページを離れることを中断します
  4. サポートIE 6 +、Firefox 2 +、Safari 3+(そしておそらくOperaがテストしなかった)

このスクリプトはPrototypeに依存していますが、別のライブラリまたはスタンドアロンに簡単に適合させることができます。

$(document).observe('dom:loaded', function(e) {
    var browser = {
        trident: !!document.all && !window.opera,
        webkit: (!(!!document.all && !window.opera) && !document.doctype) ||
            (!!window.devicePixelRatio && !!window.getMatchedCSSRules)
    };

    // Select form elements that won't bubble up delegated events (eg. onchange)
    var inputs = $('form_id').select('select, input[type="radio"], input[type="checkbox"]');

    $('form_id').observe('submit', function(e) {
        // Don't bother submitting if form not modified
        if(!$('form_id').hasClassName('modified')) {
            e.stop();
            return false;
        }
        $('form_id').addClassName('saving');
    });

    var change = function(e) {
        // Paste event fires before content has been pasted
        if(e && e.type && e.type == 'paste') {
            arguments.callee.defer();
            return false;
        }

        // Check if event actually results in changed data
        if(!e || e.type != 'change') {
            var modified = false;
            $('form_id').getElements().each(function(element) {
                if(element.tagName.match(/^textarea$/i)) {
                    if($F(element) != element.defaultValue) {
                        modified = true;
                    }
                    return;
                } else if(element.tagName.match(/^input$/i)) {
                    if(element.type.match(/^(text|hidden)$/i) && $F(element) != element.defaultValue) {
                        modified = true;
                    } else if(element.type.match(/^(checkbox|radio)$/i) && element.checked != element.defaultChecked) {
                        modified = true;
                    }
                }
            });
            if(!modified) {
                return false;
            }
        }

        // Mark form as modified
        $('form_id').addClassName('modified');

        // Enable submit/reset buttons
        $('reset_button_id').removeAttribute('disabled');
        $('submit_button_id').removeAttribute('disabled');

        // Remove event handlers as they're no longer needed
        if(browser.trident) {
            $('form_id').stopObserving('keyup', change);
            $('form_id').stopObserving('paste', change);
        } else {
            $('form_id').stopObserving('input', change);
        }
        if(browser.webkit) {
            $$('#form_id textarea').invoke('stopObserving', 'keyup', change);
            $$('#form_id textarea').invoke('stopObserving', 'paste', change);
        }
        inputs.invoke('stopObserving', 'change', arguments.callee);
    };

    $('form_id').observe('reset', function(e) {
        // Unset form modified, restart modified check...
        $('reset_button_id').writeAttribute('disabled', true);
        $('submit_button_id').writeAttribute('disabled', true);
        $('form_id').removeClassName('modified');
        startObservers();
    });

    var startObservers = (function(e) {
        if(browser.trident) {
            $('form_id').observe('keyup', change);
            $('form_id').observe('paste', change);
        } else {
            $('form_id').observe('input', change);
        }
        // Webkit apparently doesn't fire oninput in textareas
        if(browser.webkit) {
            $$('#form_id textarea').invoke('observe', 'keyup', change);
            $$('#form_id textarea').invoke('observe', 'paste', change);
        }
        inputs.invoke('observe', 'change', change);
        return arguments.callee;
    })();

    window.onbeforeunload = function(e) {
        if($('form_id').hasClassName('modified') && !$('form_id').hasClassName('saving')) {
            return 'You have unsaved content, would you really like to leave the page? All your changes will be lost.';
        }
    };

});
2
eyelidlessness

Webアプリフレームワーク(Rails、ASP.NET、Cake、symfony)を使用している場合は、ajax検証用のパッケージがあるはずです。

http://webtecker.com/2008/03/17/list-of-ajax-form-validators/

onbeforeunload()の一部のラッパーは、フォームを閉じようとしていることをユーザーに警告します。

http://pragmatig.wordpress.com/2008/03/03/protecting-userdata-from-beeing-lost-with-jquery/未保存の変更の検出

2
Gene T

Dirtyforms.jsを使用しました。私にとってはうまくいきます。

http://mal.co.nz/code/jquery-dirty-forms/

1
satyrFrost

閉じる前にユーザーに警告するには、unbeforeunloadを使用します。

window.onbeforeunload = function() {
   return "You are about to lose your form data.";
};

ページのロード時に各フィールドの値を変数に保存し、ユーザーがページをアンロードするときにそれらの値を比較します。違いが検出された場合は、保存する内容がわかりますが、終了した場合に保存されないデータをユーザーに明確に伝えることができます。

// this example uses the prototype library
// also, it's not very efficient, I just threw it together
var valuesAtLoad = [];
var valuesOnCheck = [];
var isDirty = false;
var names = [];
Event.observe(window, 'load', function() {
    $$('.field').each(function(i) {
        valuesAtLoad.Push($F(i));
    });
});

var checkValues = function() {
    var changes = [];
    valuesOnCheck = [];
    $$('.field').each(function(i) {
        valuesOnCheck.Push($F(i));
    });

    for(var i = 0; i <= valuesOnCheck.length - 1; i++ ) {
        var source = valuesOnCheck[i];
        var compare = valuesAtLoad[i];
        if( source !== compare ) {
            changes.Push($$('.field')[i]);
        }
    }

    return changes.length > 0 ? changes : [];
};

setInterval(function() { names = checkValues().pluck('id'); isDirty = names.length > 0; }, 100);

// notify the user when they exit
Event.observe(window, 'beforeunload', function(e) {
     e.returnValue = isDirty ? "you have changed the following fields: \r\n" + names + "\r\n these changes will be lost if you exit. Are you sure you want to continue?" : true;
});
1
Jared

クロスブラウザテストをいくつか行いました。

ChromeとSafariこれはいいです:

<form onchange="validate()">
...
</form>

Firefox + Chrome/Safariの場合:

<form onkeydown="validate()">
    ...
    <input type="checkbox" onchange="validate()">
</form>

チェックボックスやラジオボタンなどのアイテムには、独自のonchangeイベントリスナーが必要です。

0
Bijan

各フォームinput/select/textareaのonchangeイベントにイベントハンドラーを接続します。 「保存」ボタンを有効にする必要があるかどうかを示す変数を設定します。ダーティフォームもチェックするonunloadハンドラを作成し、フォームが送信されたら変数をリセットします。

 window.onunload = checkUnsavedPage; 
 var isDirty = false; 
 var formElements = //すべてのフォーム要素への参照を取得
 for(var i = 0; len = formElements.length; i ++){
 //各要素にonchangeイベントを追加してformChanged()
} 
 
 function formChanged(event){
 isDirty = false; 
 document.getElementById( "savebtn")。disabled = ""; 
} 
 
 function checkUnsavedPage(){
 if(isDirty){
 var isSure = confirm( "you sure?"); 
 if(!isSure){
 event.preventDefault(); 
} 
} 
} 
0
Eric Wendelin

Dylan Beattieの提案 の完全な実装を次に示します。

「保存されていないデータ」保護のためのクライアント/ JSフレームワーク?

クライアント側で動的にデータを入力する場合を除き、フォームが変更されたかどうかを判断するために初期値を保存する必要はありません(ただし、それでもフォームにdefaultプロパティを設定できます)要素)。

0
Jonny Buchanan

私も同じ課題を抱えていて、共通の解決策を考えていました。以下のコードは完全ではなく、最初の研究開発からのものです。私が使用した手順は次のとおりです。

1)次のJSを別のファイルに移動します(changeFramework.jsなど)

2)インポートしてプロジェクトに含める

3)HTMLページで、監視が必要なコントロールに、「monitorChange」クラスを追加します

4)グローバル変数「hasChanged」は、作業中のページに変更があるかどうかを示します。

<script type="text/javascript" id="MonitorChangeFramework">
// MONITOR CHANGE FRAMEWORK
// ALL ELEMENTS WITH CLASS ".monitorChange" WILL BE REGISTERED FOR CHANGE
// ON CHANGE IT WILL RAISE A FLAG
var hasChanged;

function MonitorChange() {
    hasChanged = false;
    $(".monitorChange").change(function () {
        hasChanged = true;
    });
}

以下は、このフレームワークを使用したコントロールです。

<textarea class="monitorChange" rows="5" cols="10" id="testArea"></textarea></br>
        <div id="divDrinks">
            <input type="checkbox" class="chb monitorChange" value="Tea" />Tea </br>
            <input type="checkbox" class="chb monitorChange" value="Milk" checked='checked' />Milk</br>
            <input type="checkbox" class="chb monitorChange" value="Coffee" />Coffee </br>
        </div>
        <select id="comboCar" class="monitorChange">
            <option value="volvo">Volvo</option>
            <option value="saab">Saab</option>
            <option value="mercedes">Mercedes</option>
            <option value="audi">Audi</option>
        </select>
        <button id="testButton">
            test</button><a onclick="NavigateTo()">next >>> </a>

私はこのフレームワークに大きな改善があると信じています。コメント/変更/フィードバックを歓迎します。 :)

0
Savaratkar

jQueryはフォームプラグインの変更を追跡します で作成したjQueryプラグインも確認できます。

デモ here を参照し、JSをダウンロードします here

0
brokenshard

JQueryを使用する場合は、同様の質問に対する私の回答を参照してください: 元のフォームデータが変更されていない限り、送信ボタンを無効にする

0
Justin