アプリケーションにフォームのあるページがいくつかあります。
誰かがブラウザのタブを離れたり閉じたりした場合に、保存されていないデータをフォームに残したいかどうかを確認するプロンプトが表示されるようにフォームを保護するにはどうすればよいですか?
アプリケーションにフォームのあるページがいくつかあります。
誰かがブラウザのタブを離れたり閉じたりした場合に、保存されていないデータをフォームに残したいかどうかを確認するプロンプトが表示されるようにフォームを保護するにはどうすればよいですか?
回答:
これを行うには、beforeunload
イベントを処理してnull以外の文字列を返します。
window.addEventListener("beforeunload", function (e) {
var confirmationMessage = 'It looks like you have been editing something. '
+ 'If you leave before saving, your changes will be lost.';
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});
このアプローチの問題は、フォームを送信すると、アンロードイベントも発生することです。これは、フォームを送信することを示すフラグを追加することで簡単に修正できます。
var formSubmitting = false;
var setFormSubmitting = function() { formSubmitting = true; };
window.onload = function() {
window.addEventListener("beforeunload", function (e) {
if (formSubmitting) {
return undefined;
}
var confirmationMessage = 'It looks like you have been editing something. '
+ 'If you leave before saving, your changes will be lost.';
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});
};
次に、送信時にセッターを呼び出します。
<form method="post" onsubmit="setFormSubmitting()">
<input type="submit" />
</form>
しかし、読んでください...
また、ユーザーがフォームで何も変更していないときにこのメッセージを表示したくない場合もあります。1つの解決策は、beforeunload
イベントを「ダーティ」フラグと組み合わせて使用することです。これは、本当に関連がある場合にのみプロンプトをトリガーします。
var isDirty = function() { return false; }
window.onload = function() {
window.addEventListener("beforeunload", function (e) {
if (formSubmitting || !isDirty()) {
return undefined;
}
var confirmationMessage = 'It looks like you have been editing something. '
+ 'If you leave before saving, your changes will be lost.';
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});
};
isDirty
メソッドを実装するために、さまざまなアプローチがあります。
jQueryとフォームのシリアル化を使用できますが、このアプローチにはいくつかの欠点があります。最初に、任意のフォームで機能するようにコードを変更する必要があります$("form").each()
が、最大の問題は、jQuery serialize()
が名前付きの無効化されていない要素でのみ機能するため、無効化された要素または名前のない要素を変更してもダーティフラグがトリガーされないことです。そのための回避策があります。たとえば、コントロールを有効にする代わりに読み取り専用にし、シリアル化してから再度無効にするなどです。
したがって、イベントは進むべき道のようです。キープレスを聞いてみることができます。このイベントにはいくつかの問題があります:
このchange
イベントはJavaScriptコードから設定された値に対してもトリガーされないため、仮想入力に対しても機能しません。
input
イベントをinput
textarea
select
ページ上のすべてのs(およびsとs)にバインドしても、古いブラウザでは機能せず、上記のすべてのイベント処理ソリューションと同様に、元に戻すはサポートされていません。ユーザーがテキストボックスを変更してから元に戻すか、チェックボックスをオンまたはオフにしても、フォームはダーティと見なされます。
また、特定の要素を無視するなど、より多くの動作を実装する場合は、さらに多くの作業が必要になります。
したがって、これらのソリューションと必要なすべての回避策の実装について考える前に、車輪を再発明していて、他の人がすでに解決している問題に遭遇する傾向があることを理解してください。
アプリケーションですでにjQueryを使用している場合は、独自のコードをロールする代わりに、テスト済みの保守されたコードを使用して、これらすべてにサードパーティのライブラリを使用することもできます。jQuery's Are You Sure?プラグインは素晴らしいです、彼らのデモページを見てください。それはこれと同じくらい簡単です:
<script src="jquery.are-you-sure.js"></script>
<script>
$(function() {
$('#myForm').areYouSure(
{
message: 'It looks like you have been editing something. '
+ 'If you leave before saving, your changes will be lost.'
}
);
});
</script>
Firefox 4はこのダイアログのカスタムメッセージをサポートしていないことに注意してください。2016年4月の時点で、カスタムメッセージも削除されるChrome 51がロールアウトされています。
このサイトの他の場所にもいくつかの選択肢がありますが、このようなダイアログは十分明確だと思います。
このサイトを離れますか?
行った変更は保存されない場合があります。
Leave Stay
JavaScriptのonbeforeunloadイベントを確認してください。これはMicrosoftによって導入された非標準のJavaScriptですが、ほとんどのブラウザーで機能し、onbeforeunloadのドキュメントには詳細情報と例が含まれています。
jquery経由
$('#form').data('serialize',$('#form').serialize()); // On load save form current state
$(window).bind('beforeunload', function(e){
if($('#form').serialize()!=$('#form').data('serialize'))return true;
else e=null; // i.e; if form state change show warning box, else don't show it.
});
Google JQuery Form Serialize関数を使用すると、すべてのフォーム入力が収集され、配列に保存されます。これで十分だと思います:)
contenteditable要素を含むすべての入力変更を自動的に検出する構成を必要としないユニバーサルソリューション:
"use strict";
(() => {
const modified_inputs = new Set;
const defaultValue = "defaultValue";
// store default values
addEventListener("beforeinput", (evt) => {
const target = evt.target;
if (!(defaultValue in target || defaultValue in target.dataset)) {
target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener("input", (evt) => {
const target = evt.target;
let original;
if (defaultValue in target) {
original = target[defaultValue];
} else {
original = target.dataset[defaultValue];
}
if (original !== ("" + (target.value || target.textContent)).trim()) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
});
// clear modified inputs upon form submission
addEventListener("submit", () => {
modified_inputs.clear();
// to prevent the warning from happening, it is advisable
// that you clear your form controls back to their default
// state after submission
});
// warn before closing if any inputs are modified
addEventListener("beforeunload", (evt) => {
if (modified_inputs.size) {
const unsaved_changes_warning = "Changes you made may not be saved.";
evt.returnValue = unsaved_changes_warning;
return unsaved_changes_warning;
}
});
})();
Wasim A.のシリアル化を使用するという優れたアイデアの上に構築されています。問題は、フォームの送信時に警告も表示されることでした。これはここで修正されました。
var isSubmitting = false
$(document).ready(function () {
$('form').submit(function(){
isSubmitting = true
})
$('form').data('initial-state', $('form').serialize());
$(window).on('beforeunload', function() {
if (!isSubmitting && $('form').serialize() != $('form').data('initial-state')){
return 'You have unsaved changes which will not be saved.'
}
});
})
ChromeとIE 11でテストされています。
以前の回答に基づいて、スタックオーバーフローのさまざまな場所からまとめて、実際に変更を送信したい場合に対処するために私が思いついた解決策を次に示します。
window.thisPage = window.thisPage || {};
window.thisPage.isDirty = false;
window.thisPage.closeEditorWarning = function (event) {
if (window.thisPage.isDirty)
return 'It looks like you have been editing something' +
' - if you leave before saving, then your changes will be lost.'
else
return undefined;
};
$("form").on('keyup', 'textarea', // You can use input[type=text] here as well.
function () {
window.thisPage.isDirty = true;
});
$("form").submit(function () {
QC.thisPage.isDirty = false;
});
window.onbeforeunload = window.thisPage.closeEditorWarning;
IE11がアラートを表示しないようにするには、closeEditorWarning
関数が戻る必要があるようですundefined
。
QC.thisPage.isDirty = false;
ないwindow.thisPage.isDirty = false;
?このようにして、フォームを送信しているかどうかをチェックし、警告を表示しません。私のテストではうまくいくようです。また、ほとんどのフォームにはではinput
なくがありtextarea
ます。私はかなりうまく機能する次のものを使用しました:$("form").on('keyup', 'textarea,input,select', function () {
次のワンライナーは私のために働いています。
window.onbeforeunload = s => modified ? "" : null;
アプリケーションの状態に応じて、trueまたはfalseに設定modified
するだけです。
undefined
なく返します。null
modified
次のコードはうまくいきます。id属性を介してフォーム要素の入力変更に到達する必要があります。
var somethingChanged=false;
$('#managerForm input').change(function() {
somethingChanged = true;
});
$(window).bind('beforeunload', function(e){
if(somethingChanged)
return "You made some changes and it's not saved?";
else
e=null; // i.e; if form state change show warning box, else don't show it.
});
});
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;
serialize()を使用すると、フォームの値をシリアル化してURLエンコードされたテキスト文字列を作成し、フォームが変更されたかどうかを確認してからアンロードできます。
$(document).ready(function(){
var form = $('#some-form'),
original = form.serialize()
form.submit(function(){
window.onbeforeunload = null
})
window.onbeforeunload = function(){
if (form.serialize() != original)
return 'Are you sure you want to leave?'
}
})
このリンクを参照してくださいhttps://coderwall.com/p/gny70a/alert-when-leaving-page-with-unsaved-form Vladimir Sidorenkoにより作成
serialize()
が、実際には欠点があります。
@codecasterのアイデアに追加すると、これをフォームのあるすべてのページに追加できます(私の場合、グローバルな方法で使用するため、フォームでのみこれが警告されます)に関数を変更します
if ( formSubmitting || document.getElementsByTagName('form').length == 0)
また、ログインとキャンセルボタンのリンクを含むフォーム送信をオンにすると、ユーザーがキャンセルまたはフォームを送信したときに、フォームのないすべてのページでも警告がトリガーされなくなります...
<a class="btn btn-danger btn-md" href="back/url" onclick="setFormSubmitting()">Cancel</a>
ここで詳細な説明を確認できます:http : //techinvestigations.redexp.in/comparison-of-form-values-on-load-and-before-close/ 比較・オブ・フォームの値オンロード、および-終了前
メインコード:
function formCompare(defaultValues, valuesOnClose) {
// Create arrays of property names
var aPropsFormLoad = Object.keys(defaultValues);
var aPropsFormClose = Object.keys(valuesOnClose);
// If number of properties is different,
// objects are not equivalent
if (aPropsFormLoad.length != aPropsFormClose.length) {
return false;
}
for (var i = 0; i < aPropsFormLoad.length; i++) {
var propName = aPropsFormLoad[i];
// If values of same property are not equal,
// objects are not equivalent
if (defaultValues[aPropsFormLoad]+"" !== valuesOnClose[aPropsFormLoad]+"") {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
//add polyfill for older browsers, as explained on the link above
//use the block below on load
for(i=0; i < document.forms[0].elements.length; i++){
console.log("The field name is: " + document.forms[0].elements[i].name +
" and it’s value is: " + document.forms[0].elements[i].value );
aPropsFormLoad[i] = document.forms[0].elements[i].value;
}
//create a similar array on window unload event.
//and call the utility function
if (!formCompare(aPropsOnLoad, aPropsOnClose))
{
//perform action:
//ask user for confirmation or
//display message about changes made
}
簡潔な答え:
let pageModified = true
window.addEventListener("beforeunload",
() => pageModified ? 'Close page without saving data?' : null
)
Eerik Sven Puudistによるソリューション...
var isSubmitting = false;
$(document).ready(function () {
$('form').submit(function(){
isSubmitting = true
})
$('form').data('initial-state', $('form').serialize());
$(window).on('beforeunload', function() {
if (!isSubmitting && $('form').serialize() != $('form').data('initial-state')){
return 'You have unsaved changes which will not be saved.'
}
});
})
...複雑なオブジェクト指向の設定で、変更を必要とせずに自発的に仕事をしました。
私が適用した唯一の変更は、 "formForm"( 'form'-> '#formForm')と呼ばれる具象フォーム(ファイルごとに1つのフォームのみ)を参照することでした。
<form ... id="formForm" name="formForm" ...>
特に良いのは、送信ボタンが「そのままにされている」という事実です。
さらに、Firefoxの最新バージョン(2019年2月7日現在)でも動作します。
Eli Greyのユニバーサルソリューションをテストしました。
'use strict';
(() => {
const modified_inputs = new Set();
const defaultValue = 'defaultValue';
// store default values
addEventListener('beforeinput', evt => {
const target = evt.target;
if (!(defaultValue in target.dataset)) {
target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener('input', evt => {
const target = evt.target;
let original = target.dataset[defaultValue];
let current = ('' + (target.value || target.textContent)).trim();
if (original !== current) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
});
addEventListener(
'saved',
function(e) {
modified_inputs.clear()
},
false
);
addEventListener('beforeunload', evt => {
if (modified_inputs.size) {
const unsaved_changes_warning = 'Changes you made may not be saved.';
evt.returnValue = unsaved_changes_warning;
return unsaved_changes_warning;
}
});
})();
彼への変更は削除され、使用target[defaultValue]
のみtarget.dataset[defaultValue]
、実際のデフォルト値を保存するにれます。
また、保存アクションが成功したときに自分で「保存」イベントがトリガーされる「保存」イベントリスナーを追加しました。
ただし、この「ユニバーサル」ソリューションはブラウザーでのみ機能し、アプリのWebビューでは機能しません(例:wechatブラウザー)。
wechatブラウザーで(部分的に)動作するようにするために、もう一度別の改善を行いました。
'use strict';
(() => {
const modified_inputs = new Set();
const defaultValue = 'defaultValue';
// store default values
addEventListener('beforeinput', evt => {
const target = evt.target;
if (!(defaultValue in target.dataset)) {
target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener('input', evt => {
const target = evt.target;
let original = target.dataset[defaultValue];
let current = ('' + (target.value || target.textContent)).trim();
if (original !== current) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
if(modified_inputs.size){
const event = new Event('needSave')
window.dispatchEvent(event);
}
});
addEventListener(
'saved',
function(e) {
modified_inputs.clear()
},
false
);
addEventListener('beforeunload', evt => {
if (modified_inputs.size) {
const unsaved_changes_warning = 'Changes you made may not be saved.';
evt.returnValue = unsaved_changes_warning;
return unsaved_changes_warning;
}
});
const ua = navigator.userAgent.toLowerCase();
if(/MicroMessenger/i.test(ua)) {
let pushed = false
addEventListener('needSave', evt => {
if(!pushed) {
pushHistory();
window.addEventListener("popstate", function(e) {
if(modified_inputs.size) {
var cfi = confirm('确定要离开当前页面嘛?' + JSON.stringify(e));
if (cfi) {
modified_inputs.clear()
history.go(-1)
}else{
e.preventDefault();
e.stopPropagation();
}
}
}, false);
}
pushed = true
});
}
function pushHistory() {
var state = {
title: document.title,
url: "#flag"
};
window.history.pushState(state, document.title, "#flag");
}
})();
私はそれを別の方法で行いました。誰かが助けを借りられるようにここで共有し、Chromeでのみテストしました。
いくつかの変更がある場合にのみ、タブを閉じる前にユーザーに警告します。
<input type="text" name="field" value="" class="onchange" />
var ischanged = false;
$('.onchange').change(function () {
ischanged = true;
});
window.onbeforeunload = function (e) {
if (ischanged) {
return "Make sure to save all changes.";
}
};
正常に機能しますが、別の問題が発生しました。フォームを送信すると、不要な警告が表示され、多くの回避策が表示されました。これは、onbeforeunloadがonsubmitの前に発生するためonbeforeunload = null
、のようなonsubmitイベントで処理できないためです。これらの両方のイベントの前に送信ボタンのonclickイベントが発生するため、コードを更新しました
var isChanged = false;
var isSubmit = false;
window.onbeforeunload = function (e) {
if (isChanged && (!isSubmit)) {
return "Make sure to save all changes.";
}
};
$('#submitbutton').click(function () {
isSubmit = true;
});
$('.onchange').change(function () {
isChanged = true;
});
まず第一に、ほとんどのブラウザはデフォルトでこの機能を持っています。そして、なぜこれが必要なのですか?フォームの同期を維持しないのはなぜですか?つまり、ユーザーからの送信を待たずに、変更を保存します。Googleコンタクトのように。もちろん、フォーム内のすべてのフィールドのみが必須である場合。ユーザーは、必要なものであるかどうかを考えるために立ち去る機会なしに、何かを強制的に満たすことを好まない。:)
save it on any change without waiting any submitting from user
あなたはいつもこれをしたくありません。場合によっては、ユーザーが多くの変更を加えてから、保存したいことを確認して確認したいことがあります。