ダーティフォームを検出するための軽量のjQueryプラグインを作成していますが、イベントに問題があります。次のコードでわかるように、プラグインはイベントリスナーを「beforeunload」にアタッチし、フォームがダーティであるかどうかをテストしてポップアップを生成します。
そのフォームの「送信」にアタッチされた別のイベントリスナーもあり、理論的にはその特定のフォームの「アンロード前」リスナーを削除する必要があります(つまり、送信している現在のフォームの汚れをテストする必要はありませんが、ページ上の他のフォームは)。
たくさんのconsole.logステートメントを挿入してデバッグを試みましたが、うまくいきませんでした。考え?
// Checks if any forms are dirty if leaving page or submitting another forms
// Usage:
// $(document).ready(function(){
// $("form.dirty").dirtyforms({
// excluded: $('#name, #number'),
// message: "please don't leave dirty forms around"
// });
// });
(function($) {
////// private variables //////
var instances = [];
////// general private functions //////
function _includes(obj, arr) {
return (arr._indexOf(obj) != -1);
}
function _indexOf(obj) {
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (obj, fromIndex) {
if (fromIndex == null) {
fromIndex = 0;
} else if (fromIndex < 0) {
fromIndex = Math.max(0, this.length + fromIndex);
}
for (var i = fromIndex, j = this.length; i < j; i++) {
if (this[i] === obj)
return i;
}
return -1;
};
}
}
////// the meat of the matter //////
// DirtyForm initialization
var DirtyForm = function(form, options) {
// unique name for testing purposes
this.name = "instance_" + instances.length
this.form = form;
this.settings = $.extend({
'excluded' : [],
'message' : 'You will lose all unsaved changes.'
}, options);
// remember intial state of form
this.memorize_current();
// activate dirty tracking, but disable it if this form is submitted
this.enable();
$(this.form).on('submit', $.proxy(this.disable, this));
// remember all trackable forms
instances.push(this);
}
// DirtyForm methods
DirtyForm.prototype = {
memorize_current: function() {
this.originalForm = this.serializeForm();
},
isDirty: function() {
var currentForm = this.serializeForm();
console.log("isDirty called...")
return (currentForm != this.originalForm);
},
enable: function() {
$(window).on('beforeunload', $.proxy(this.beforeUnloadListener, this));
console.log("enable called on " + this.name)
},
disable: function(e) {
$(window).off('beforeunload', $.proxy(this.beforeUnloadListener, this));
console.log("disable called on " + this.name)
},
disableAll: function() {
$.each(instances, function(index, instance) {
$.proxy(instance.disable, instance)
});
},
beforeUnloadListener: function(e) {
console.log("beforeUnloadListener called on " + this.name)
console.log("... and it is " + this.isDirty())
if (this.isDirty()) {
e.returnValue = this.settings.message;
return this.settings.message;
}
},
setExcludedFields: function(excluded) {
this.settings.excluded = excluded;
this.memorize_current();
this.enable();
},
serializeForm: function() {
var blacklist = this.settings.excludes
var filtered = [];
var form_elements = $(this.form).children();
// if element is not in the excluded list
// then let's add it to the list of filtered form elements
if(blacklist) {
$.each(form_elements, function(index, element) {
if(!_includes(element, blacklist)) {
filtered.push(element);
}
});
return $(filtered).serialize();
} else {
return $(this.form).serialize();
}
}
};
////// the jquery plugin part //////
$.fn.dirtyForms = function(options) {
return this.each(function() {
new DirtyForm(this, options);
});
};
})(jQuery);
[編集]
ハンドラーを識別するためにjQueryの.on()新しい名前空間機能を使用してこれを修正することになりました。問題は、.off()へのハンドラー引数として新しい無名関数を渡していたことでした。あなたの解決策をありがとう@FelixKling!
this.id = instances.length
[...]
enable: function () {
$(window).on('beforeunload.' + this.id, $.proxy(this.beforeUnloadListener, this));
},
disable: function () {
$(window).off('beforeunload.' + this.id);
},