1

ダーティフォームを検出するための軽量の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);
    },
4

1 に答える 1

1

呼び出すたびに、新しい関数$.proxy()が返されます。したがって、

$(window).off('beforeunload', $.proxy(this.beforeUnloadListener, this));

バインドされていない関数のバインドを解除しようとしているため、効果はありません。

$.proxy後でバインドを解除できるように、で作成された関数への参照を保存する必要があります。

enable: function() {
    this.beforeUnloadListener = $.proxy(DirtyForm.prototype.beforeUnloadListener, this);
    $(window).on('beforeunload', this.beforeUnloadListener);
    console.log("enable called on " + this.name)
},

disable: function(e) {
    $(window).off('beforeunload', this.beforeUnloadListener);
    console.log("disable called on " + this.name)
},
于 2012-07-25T15:36:05.950 に答える