保存されていない変更の検出


94

ASP .Netアプリケーションに「未保存の変更」プロンプトを実装する必要があります。ユーザーがWebフォームのコントロールを変更し、保存する前に移動しようとすると、変更が保存されていないことを警告するプロンプトが表示され、キャンセルして現在のページにとどまるオプションが表示されます。ユーザーがいずれのコントロールにも触れていない場合、プロンプトは表示されません。

これをJavaScriptで実装するのが理想的ですが、独自のコードを展開する前に、これを実現するための既存のフレームワークや推奨される設計パターンはありますか?理想的には、最小限の変更で複数のページにわたって簡単に再利用できる何かが欲しいです。


私はasp.netだけでなく、同じことに興味がありますが、一般的な解決策はそこにあると思います。
マイケルニール

以下に投稿したソリューションは、プレーンHTML / Javscriptで使用できます。「分離コード」をHTMLタグ自体の属性に変更するだけです。
ウェイン、

私はここで5年遅れていることを知っていますが、以前の解決策を改善できると思います...以下の回答を修正して更新しました。
Skibulk 2013年

回答:


96

jQueryの使用:

var _isDirty = false;
$("input[type='text']").change(function(){
  _isDirty = true;
});
// replicate for other input types and selects

必要に応じてonunload/ onbeforeunloadメソッドと組み合わせます。

コメントから、以下はコードを複製せずにすべての入力フィールドを参照します。

$(':input').change(function () {

を使用する$(":input")と、すべての入力、textarea、select、およびbutton要素が参照されます。


32
私がこのソリューションを使用したので賛成票を投じますが、少し簡単な方法があります。$( ':input')。change(function(){を使用すると、すべての入力フィールドで機能します。他の入力タイプで複製する必要はありません。$( ":input")は、すべての入力、textareaを選択します、選択、およびボタン要素
CodeClimber '29

5
これはFirefoxで問題が発生する可能性があります。ユーザーがフォームを変更し、ブラウザの更新ボタンをクリックすると、ページがリロードされ、Firefoxはchange-eventを起動せずにユーザーの以前のエントリをフォームに入力します。これでフォームはダーティになりますが、ユーザーが別の編集を行わない限り、フラグは設定されません。
Oskar Berggren 2014

8
ほんのヒントです。フォームが汚れていても、実際に変更されたデータが含まれているとは限りません。使いやすさを高めるには、実際に古いデータと新しいデータを比較する必要があります;)
Slava Fomin II

ことを念頭に置いてくださいchangeに対し、フォーカスを失う/去る上で一度テキスト入力トリガのためにinputすべてのキーストロークのトリガー。(私は通常、複数のイベントを防ぐために、changeJQuery と一緒に使用しone()ます。)
SpazzMarticus 2018年

46

パズルのピース:

/**
 * Determines if a form is dirty by comparing the current value of each element
 * with its default value.
 *
 * @param {Form} form the form to be checked.
 * @return {Boolean} <code>true</code> if the form is dirty, <code>false</code>
 *                   otherwise.
 */
function formIsDirty(form) {
  for (var i = 0; i < form.elements.length; i++) {
    var element = form.elements[i];
    var type = element.type;
    if (type == "checkbox" || type == "radio") {
      if (element.checked != element.defaultChecked) {
        return true;
      }
    }
    else if (type == "hidden" || type == "password" ||
             type == "text" || type == "textarea") {
      if (element.value != element.defaultValue) {
        return true;
      }
    }
    else if (type == "select-one" || type == "select-multiple") {
      for (var j = 0; j < element.options.length; j++) {
        if (element.options[j].selected !=
            element.options[j].defaultSelected) {
          return true;
        }
      }
    }
  }
  return false;
}

そしてもう一つ

window.onbeforeunload = function(e) {
  e = e || window.event;  
  if (formIsDirty(document.forms["someForm"])) {
    // For IE and Firefox
    if (e) {
      e.returnValue = "You have unsaved changes.";
    }
    // For Safari
    return "You have unsaved changes.";
  }
};

それをすべて包み込み、何を手に入れますか?

var confirmExitIfModified = (function() {
  function formIsDirty(form) {
    // ...as above
  }

  return function(form, message) {
    window.onbeforeunload = function(e) {
      e = e || window.event;
      if (formIsDirty(document.forms[form])) {
        // For IE and Firefox
        if (e) {
          e.returnValue = message;
        }
        // For Safari
        return message;
      }
    };
  };
})();

confirmExitIfModified("someForm", "You have unsaved changes.");

また、beforeunloadイベントハンドラの登録を変更して、LIBRARY_OF_CHOICEのイベント登録を使用することもできます。


Firefox 9.0.1ではメッセージが表示されません。「ページから移動してもよろしいですか?一部のデータが失われる可能性があります」というデフォルトのメッセージがあります(これは多かれ少なかれ、英語以外のブラウザが表示するメッセージの翻訳です)
Romain Guidoux

1
このスクリプトはすばらしいですが、私にとっては、フォームの送信時に警告メッセージも表示されました。onClick(onclick="window.onbeforeunload=null")でイベントのバインドを解除してこれを修正しました
Joost

1
ダーティビットを設定するのではなく、defaultValueなどのプロパティを比較するアプローチが好きです。つまり、誰かがフィールドを変更し、それを元に戻した場合、フォームはダーティとして報告されません。
thelem 2014年

おかげで、これは素晴らしいです。jqueryへの登録方法を教えてください。同じHTMLページにこのスクリプトがない場合、起動しません。外部.jsファイル内にある場合、機能しません。
Shiva Naru

17

.aspxページでは、フォーム情報が「ダーティ」かどうかを判断するJavaScript関数が必要です

<script language="javascript">
    var isDirty = false;

    function setDirty() {
        isDirty = true;
    }

    function checkSave() {
        var sSave;
        if (isDirty == true) {
            sSave = window.confirm("You have some changes that have not been saved. Click OK to save now or CANCEL to continue without saving.");
            if (sSave == true) {
                document.getElementById('__EVENTTARGET').value = 'btnSubmit';
                document.getElementById('__EVENTARGUMENT').value = 'Click';  
                window.document.formName.submit();
            } else {
                 return true;
            }
        }
    }
</script>
<body class="StandardBody" onunload="checkSave()">

コードビハインドで、入力フィールドにトリガーを追加し、送信/キャンセルボタンをリセットします。

btnSubmit.Attributes.Add("onclick", "isDirty = 0;");
btnCancel.Attributes.Add("onclick", "isDirty = 0;");
txtName.Attributes.Add("onchange", "setDirty();");
txtAddress.Attributes.Add("onchange", "setDirty();");
//etc..

9

以下は、ブラウザのonbeforeunload関数とjqueryを使用して、すべてのonchangeイベントをキャプチャします。ITは、変更が発生したことを示すフラグをリセットするための送信またはリセットボタンも探します。

dataChanged = 0;     // global variable flags unsaved changes      

function bindForChange(){    
     $('input,checkbox,textarea,radio,select').bind('change',function(event) { dataChanged = 1})
     $(':reset,:submit').bind('click',function(event) { dataChanged = 0 })
}


function askConfirm(){  
    if (dataChanged){ 
        return "You have some unsaved changes.  Press OK to continue without saving." 
    }
}

window.onbeforeunload = askConfirm;
window.onload = bindForChange;

私は、チェックボックスを配置したときこんにちはコリン・リセットは動作しませんでした、最初に私はそれを確認して、再度チェックを外しますが、アラートは、ユーザーがチェックボックスオフにするとき、私はリセットすることができますどのように、解雇された
開発者を

8

皆様のご返信ありがとうございます。最終的に、JQueryとProtect-Dataプラグインを使用してソリューションを実装しました。これにより、ページ上のすべてのコントロールに監視を自動的に適用できます。

ただし、特にASP .Netアプリケーションを扱う場合には、いくつかの注意点があります。

  • ユーザーがキャンセルオプションを選択すると、doPostBack関数はJavaScriptエラーをスローします。doPostBack内の.submit呼び出しの周りに手動でtry-catchを配置して抑制しなければなりませんでした。

  • 一部のページでは、ユーザーは同じページへのポストバックを実行するアクションを実行できますが、それは保存ではありません。これにより、JavaScriptロジックがリセットされるため、ポストバックの後で何かが変更された可能性があるとは考えられません。ページと共にポストバックされる非表示のテキストボックスを実装する必要があり、データがダーティかどうかを示す単純なブール値を保持するために使用されます。これはポストバック間で永続化されます。

  • [保存]ボタンなど、ページのいくつかのポストバックがダイアログをトリガーしないようにする必要がある場合があります。この場合、JQueryを使用して、window.onbeforeunloadをnullに設定するOnClick関数を追加できます。

うまくいけば、これは同様の何かを実装する必要がある他の誰にとっても役に立ちます。


7

特定のページで複数のフォームをサポートする一般的なソリューション(プロジェクトにコピーして貼り付けるだけ

$(document).ready(function() {
    $('form :input').change(function() {
        $(this).closest('form').addClass('form-dirty');
    });

    $(window).bind('beforeunload', function() {
        if($('form:not(.ignore-changes).form-dirty').length > 0) {
            return 'You have unsaved changes, are you sure you want to discard them?';
        }
    });

    $('form').bind('submit',function() {
        $(this).closest('form').removeClass('form-dirty');
        return true;
    });
});

注:このソリューションは、他のソリューションと組み合わせて、一般的な統合ソリューションを作成します。

特徴:

  • コピーしてアプリに貼り付けるだけです。
  • 複数のフォームをサポートします。
  • アクションにダーティなフォームがあるため、アクションをダーティなフォームにスタイル設定または作成できます。
  • クラス 'ignore-changes'を追加することで、一部のフォームを除外できます。

これはうまく機能しますが、追加されたクラスを、_isDirtyAaron Powellによって説明されている変数に置き換えます。JavaScriptの変数の代わりに、HTMLにクラスを追加したり削除したりする必要はありませんでした。
マーティン

1
ここでhtmlクラスを使用するアイデアは、単純に変数を使用して一般的なapplication-wize js / htmlソリューションを作成することができないため、各フォームに 'dirty'属性を添付することです。
MhdSyrwan 2016年

1
言い換えると、変数の使用は単一のフォームに対してのみ機能します。それ以外の場合は、ダーティフラグの配列が必要になるため、ソリューションがより複雑になります。
MhdSyrwan 2016年

1
その点を説明してくれてありがとう。私の必要性は、単一のフォームページだけでした。
マーティン

6

次のソリューションはプロトタイプ(FF、IE 6、Safariでテスト済み)で機能します。汎用フォームオブザーバー(フォームのフィールドが変更されたときにform:changedを起動する)を使用します。これは他のオブジェクトにも使用できます。

/* use this function to announce changes from your own scripts/event handlers.
 * Example: onClick="makeDirty($(this).up('form'));"
 */
function makeDirty(form) {
    form.fire("form:changed");
}

function handleChange(form, event) {
    makeDirty(form);
}

/* generic form observer, ensure that form:changed is being fired whenever
 * a field is being changed in that particular for
 */
function setupFormChangeObserver(form) {
    var handler = handleChange.curry(form);

    form.getElements().each(function (element) {
        element.observe("change", handler);
    });
}

/* installs a form protector to a form marked with class 'protectForm' */
function setupProtectForm() {
    var form = $$("form.protectForm").first();

    /* abort if no form */
    if (!form) return;

    setupFormChangeObserver(form);

    var dirty = false;
    form.observe("form:changed", function(event) {
        dirty = true;
    });

    /* submitting the form makes the form clean again */
    form.observe("submit", function(event) {
        dirty = false;
    });

    /* unfortunatly a propper event handler doesn't appear to work with IE and Safari */
    window.onbeforeunload = function(event) {
        if (dirty) {
            return "There are unsaved changes, they will be lost if you leave now.";
        }
    };
}

document.observe("dom:loaded", setupProtectForm);

6

簡単なjavascript / jqueryソリューションを次に示します。これは、ユーザーによる「取り消し」を考慮し、アプリケーションを簡単にするために関数内にカプセル化されており、送信時に誤作動しません。関数を呼び出してフォームのIDを渡すだけです。

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

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

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

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

2
私はあなたのアプローチを試みました。それはChromeとFirefoxのために動作しますが、...それはiPadのミニのSafariのために働くことはできません
のDimitry K

4

私は最近、dirtyFormsと呼ばれるオープンソースのjQueryプラグインに貢献しました。

プラグインは、動的に追加されたHTMLで動作するように設計されており、複数のフォームをサポートし、ほぼすべてのダイアログフレームワークをサポートでき、ダイアログをアンロードする前にブラウザーにフォールバックし、カスタムエディターからダーティステータスを取得できるようにプラグイン可能なヘルパーフレームワークを備えています(tinyMCEプラグインが含まれています)。 、iFrame内で機能し、ダーティステータスは自由に設定またはリセットできます。

https://github.com/snikch/jquery.dirtyforms


4

jQueryを使用してフォームの変更を検出することは非常に簡単です。

var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed

// check for form changes
if ($('#formId').serialize() != formInitVal) {
    // show confirmation alert
}

2

上記のSlaceの提案を拡張して、ほとんどの編集可能な要素を含め、特定の要素(ここでは「srSearch」と呼ばれるCSSスタイルを使用)がダーティフラグを設定しないようにしました。

<script type="text/javascript">
        var _isDirty = false;
        $(document).ready(function () {            

            // Set exclude CSS class on radio-button list elements
            $('table.srSearch input:radio').addClass("srSearch");

            $("input[type='text'],input[type='radio'],select,textarea").not(".srSearch").change(function () {
                _isDirty = true;
            });
        });

        $(window).bind('beforeunload', function () {
            if (_isDirty) {
                return 'You have unsaved changes.';
            }
        });        


2
      var unsaved = false;
    $(":input").change(function () {         
        unsaved = true;
    });

    function unloadPage() {         
        if (unsaved) {             
          alert("You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?");
        }
    } 

window.onbeforeunload = unloadPage;



0

1つの方法は、変更を追跡できるように配列を使用して変数を保持することです。

変更検出する非常に単純な方法を次に示しますが、残りはそれほどエレガントではありません。

Farfetched Blogによる、かなりシンプルで小さな別の方法:

<body onLoad="lookForChanges()" onBeforeUnload="return warnOfUnsavedChanges()">
<form>
<select name=a multiple>
 <option value=1>1
 <option value=2>2
 <option value=3>3
</select>
<input name=b value=123>
<input type=submit>
</form>

<script>
var changed = 0;
function recordChange() {
 changed = 1;
}
function recordChangeIfChangeKey(myevent) {
 if (myevent.which && !myevent.ctrlKey && !myevent.ctrlKey)
  recordChange(myevent);
}
function ignoreChange() {
 changed = 0;
}
function lookForChanges() {
 var origfunc;
 for (i = 0; i < document.forms.length; i++) {
  for (j = 0; j < document.forms[i].elements.length; j++) {
   var formField=document.forms[i].elements[j];
   var formFieldType=formField.type.toLowerCase();
   if (formFieldType == 'checkbox' || formFieldType == 'radio') {
    addHandler(formField, 'click', recordChange);
   } else if (formFieldType == 'text' || formFieldType == 'textarea') {
    if (formField.attachEvent) {
     addHandler(formField, 'keypress', recordChange);
    } else {
     addHandler(formField, 'keypress', recordChangeIfChangeKey);
    }
   } else if (formFieldType == 'select-multiple' || formFieldType == 'select-one') {
    addHandler(formField, 'change', recordChange);
   }
  }
  addHandler(document.forms[i], 'submit', ignoreChange);
 }
}
function warnOfUnsavedChanges() {
 if (changed) {
  if ("event" in window) //ie
   event.returnValue = 'You have unsaved changes on this page, which will be discarded if you leave now. Click "Cancel" in order to save them first.';
  else //netscape
   return false;
 }
}
function addHandler(target, eventName, handler) {
 if (target.attachEvent) {
  target.attachEvent('on'+eventName, handler);
 } else {
  target.addEventListener(eventName, handler, false);
 }
}
</script>

0

IEでdocument.readyは正しく機能せず、入力の値を更新します。

したがって、IEブラウザーも処理するdocument.ready関数内でloadイベントをバインドする必要があります。

以下は、document.ready関数内に配置する必要があるコードです。

 $(document).ready(function () {
   $(window).bind("load", function () { 
    $("input, select").change(function () {});
   });
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.