1

我正在编写一个轻量级的 jQuery 插件来检测脏表单,但在处理事件时遇到了一些问题。正如您在下面的代码中看到的那样,该插件将一个事件侦听器附加到“beforeunload”,以测试表单是否脏并生成一个弹出窗口。

还有另一个事件监听器附加到该表单的“提交”,理论上应该删除该特定表单的“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 回答