自定义jQuery插件:事件的行为不符合预期

时间:2012-07-25 14:30:20

标签: javascript events

我正在编写一个轻量级的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);
    },

1 个答案:

答案 0 :(得分: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)
},