侦听对象的更改在没有嵌套事件侦听器的数组中

时间:2012-11-12 09:38:49

标签: javascript jquery event-bubbling event-listener

(免责声明:这个问题比标题所暗示的要简单得多!)

我的程序架构中出现了一个重新出现的问题,它听取了对模型的更改。

我已经设置了一些集合(很像Backbone.js),它们只是围绕数组构建的轻量级类。该系列是一系列模型。当用户对模型进行更改时,我最终会遇到可怕的嵌套事件。这是一个我想避免的典型,简单的场景:

$("#user-details").on("click", "#save", onSaveClick);

function onSaveClick()
{       
    var givennames = $("#givenname").val(),

    // Get the model from the collection
    var user = users.get($(this).attr("data-id"));

    // set the property
    user.set("GivenNames", givennames);

    // listen for the save event
    $(user).on("save", function(){
        // Alert the view
    });

    user.save();    // save event triggers a "save" event on the model
}

如果同一用户被保存两次,则会多次添加/触发事件。对此有更好的模式吗?

事件是否应该通过集合冒泡并以这种方式处理?

这是一个实际的例子(我最惭愧的一个)

$("#user-devices").on("click", ".device button", function(){

            var button = $(this),
                deviceId = $(this).closest(".device").attr("data-id"),
                device = userDevices.get(deviceId);


            $(device).on("activate", function(event, device){

                button.removeClass("btn-danger").addClass("btn-success")

                $("#activation-dialog").modal("show");
            })

            if (device.get("Active") === "1")
            {
                $("#reactivation-warning-dialog").modal("show");
                $("#reactivation-warning-dialog .btn-primary").on("click", function(){
                    device.activate();
                });

            }
            else
            {
                device.activate();  
            }
        });

2 个答案:

答案 0 :(得分:1)

您可以检查save事件是否已绑定,在这种情况下不再绑定它,如下所示:

// if no 'save' event already binded
if (!$(user).data('events') || !$(user).data('events').save) { 
    // listen for the save event
    $(user).on("save", function(){
        // Alert the view
    });
}

见工作example


为了将一些糖放在最上面,我们可以将“检查是否存在事件”逻辑放入自定义jquery伪选择器中,其定义如下:

$.expr[':'].hasEvent = function(obj, idx, meta, stack) {
    return ($(obj).data('events') != undefined 
            && $(obj).data('events')[meta[3]] != undefined);
};

然后你可以这样使用它:

$(user).not(":hasEvent(save)").on("save", function(){
     // Alert the view
});

工作example


更新JQUERY> = 1.8

jQuery 1.8 开始,对事件对象进行了一些更改,这使我的上述代码无法正常工作,请参阅jQuery 1.8 release notes中的摘录:

  

$(element).data(“events”):在1.6版本中,jQuery将其分离   来自用户数据的内部数据,以防止名称冲突。   但是,有些人正在使用内部无证件“事件”   数据结构,所以我们仍然可以检索通过   .data()。这在1.8中已被删除,但您仍然可以访问   通过$._data(element, "events")进行调试的事件数据。   请注意,这不是受支持的公共接口;实际数据   结构可能会因版本不同而发生变化。

所以我在这里发布了以上示例的 jQuery> = 1.8 更新版本:

检查保存事件是否已绑定,在这种情况下不再绑定它:

// if no 'save' event already binded
if (!$._data(user, 'events') || !$._data(user, 'events').save) { 
    // listen for the save event
    $(user).on("save", function(){
        // Alert the view
    });
}

自定义jquery伪选择器:

$.expr[':'].hasEvent = function(obj, idx, meta, stack) {
    return ($._data(obj, 'events') != undefined 
            && $._data(obj, 'events')[meta[3]] != undefined);
};

答案 1 :(得分:0)

我同意嵌套的监听器有点奇怪 - 毕竟,你已经在一个保存监听器中,只是为了点击按钮而不是模型对象。

我想我会重写这样的代码:

$("#user-devices").on("click", ".device button", function(){

    var button = $(this),
        deviceId = $(this).closest(".device").attr("data-id"),
        device = userDevices.get(deviceId);

    if (device.get("Active") === "1")
    {
        $("#reactivation-warning-dialog").modal("show");
        $("#reactivation-warning-dialog .btn-primary").on("click", device.activate);

    }
    else
    {
        device.activate();
    }
});

//in the device "class"
{ 
   ...
   ...

   activate: function() {
     //persist and stuf.

     //on success...
     this.render();
   }

   render: function() {
      button.removeClass("btn-danger").addClass("btn-success");
      $("#activation-dialog").modal("show");
   }
}