ASP .Netアプリケーションで「未保存の変更」プロンプトを実装する必要があります。ユーザーがWebフォームのコントロールを変更し、保存する前に移動しようとすると、プロンプトが表示されます未保存の変更があることを警告し、キャンセルして現在のページにとどまるオプションをユーザーに与えますユーザーがコントロールに触れていない場合、プロンプトは表示されません。
理想的にはこれをJavaScriptで実装したいのですが、独自のコードを展開する前に、これを実現するための既存のフレームワークや推奨デザインパターンはありますか?理想的には、最小限の変更で複数のページで簡単に再利用できるものが欲しいです。
JQueryの使用:
_var _isDirty = false;
$("input[type='text']").change(function(){
_isDirty = true;
});
// replicate for other input types and selects
_
必要に応じてonunload
/onbeforeunload
メソッドと組み合わせます。
コメントから、以下はコードを複製せずにすべての入力フィールドを参照します。
_$(':input').change(function () {
_
$(":input")
を使用すると、すべてのinput、textarea、select、およびbutton要素が参照されます。
パズルのピース:
/**
* 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
のイベント登録。
.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..
以下は、ブラウザの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;
全員に返信いただきありがとうございます。最終的に、JQueryと Protect-Data プラグインを使用してソリューションを実装しました。これにより、ページ上のすべてのコントロールに監視を自動的に適用できます。
ただし、特にASP .Netアプリケーションを扱う場合には、いくつかの注意事項があります。
ユーザーがキャンセルオプションを選択すると、doPostBack関数はJavaScriptエラーをスローします。それを抑制するには、doPostBack内の.submit呼び出しの周りに手動でtry-catchを配置する必要がありました。
一部のページでは、ユーザーは同じページへのポストバックを実行するアクションを実行できますが、保存ではありません。これにより、JavaScriptロジックがリセットされるため、ポストバック後に何か変更があったとしても何も変化していないと考えられます。ページと共にポストバックされる非表示のテキストボックスを実装する必要があり、データがダーティかどうかを示す単純なブール値を保持するために使用されます。これはポストバック間で持続します。
[保存]ボタンなど、ページ上のいくつかのポストバックがダイアログをトリガーしないようにすることができます。この場合、JQueryを使用して、window.onbeforeunloadをnullに設定するOnClick関数を追加できます。
うまくいけば、これは似たようなものを実装しなければならない他の誰にとっても役立つでしょう。
次の解決策はプロトタイプで機能します(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);
特定のページで複数のフォームをサポートする一般的なソリューション(プロジェクトにコピーして貼り付けてください)
$(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;
});
});
注:このソリューションは、他のソリューションと組み合わせて一般的な統合ソリューションを作成します。
特徴:
これは、簡単なjavascript/jqueryソリューションです。ユーザーによる「取り消し」を説明し、アプリケーションを簡単にするために関数内にカプセル化され、送信時に失敗しません。関数を呼び出して、フォームのIDを渡すだけです。
この関数は、ページが読み込まれたときに一度、ユーザーがページを離れる前にフォームをシリアル化します。 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');
});
JQueryを使用してフォームの変更を検出するのは非常に簡単です。
var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed
// check for form changes
if ($('#formId').serialize() != formInitVal) {
// show confirmation alert
}
最近、 dirtyForms と呼ばれるオープンソースのjQueryプラグインに貢献しました。
プラグインは、動的に追加されたHTMLで動作するように設計されており、複数のフォームをサポートし、実質的にすべてのダイアログフレームワークをサポートし、ダイアログをアンロードする前にブラウザーにフォールバックし、カスタムエディターからダーティステータスの取得をサポートするプラグ可能なヘルパーフレームワークを備えています(tinyMCEプラグインが含まれています) 、iFrame内で動作し、ダーティステータスは自由に設定またはリセットできます。
上記の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.';
}
});
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;
これは、Fleegix.jsプラグインfleegix.form.diff
(- http://js.fleegix.org/plugins/form/diff )が作成されたものです。 fleegix.form.toObject
(- http://js.fleegix.org/ref#fleegix.form.toObject )を使用して、ロード時にフォームの初期状態をシリアル化し、保存しますそれを変数に入れ、アンロード時にfleegix.form.diff
を使用して現在の状態と比較します。やさしい。
In IE document.readyは適切に動作せず、入力の値を更新します。
そのため、IE browserも処理するdocument.ready関数内でloadイベントをバインドする必要があります。
以下は、document.ready関数内に配置する必要があるコードです。
$(document).ready(function () {
$(window).bind("load", function () {
$("input, select").change(function () {});
});
});
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>