什么是一个很好的通用兄弟控制Javascript通信策略?

时间:2010-03-09 18:27:29

标签: javascript event-handling

我正在构建一个由多个控件组成的网页,并尝试提出一个有效的通用客户端兄弟控制通信模型。其中一个控件是菜单控件。每当在这里单击一个项目时,我想要公开其他控件可以订阅的自定义客户端事件,以便我可以实现松散耦合的兄弟控制通信模型。

为此,我创建了一个简单的Javascript事件集合类(下面的代码),它类似于控件事件注册和事件订阅的集线器。这段代码当然可以完成工作,但我的问题是,在最佳实践或工具方面有更好的方法来做到这一点,还是这只是一个傻瓜的事情?

/// Event collection object - acts as the hub for control communication.
function ClientEventCollection()
{
    this.ClientEvents = {};
    this.RegisterEvent = _RegisterEvent;
    this.AttachToEvent = _AttachToEvent;
    this.FireEvent = _FireEvent;

    function _RegisterEvent(eventKey)
    {
        if (!this.ClientEvents[eventKey])
           this.ClientEvents[eventKey] = [];
    }

    function _AttachToEvent(eventKey, handlerFunc)
    {
        if (this.ClientEvents[eventKey])
             this.ClientEvents[eventKey][this.ClientEvents[eventKey].length] = handlerFunc;
    }

    function _FireEvent(eventKey, triggerId, contextData )
    {
        if (this.ClientEvents[eventKey])
        {
            for (var i = 0; i < this.ClientEvents[eventKey].length; i++) 
            {
                var fn = this.ClientEvents[eventKey][i];
                if (fn)
                    fn(triggerId, contextData);
           }
        }
    }
}

// load new collection instance.
var myClientEvents = new bsdClientEventCollection();

// register events specific to the control that owns it, this will be emitted by each respective control.
myClientEvents.RegisterEvent("menu-item-clicked");

以下是源代码和订阅者控件使用上述代码的部分。

// menu control 
$(document).ready(function() 
{    
    $(".menu > a").click( function(event) 
                    { 
                        //event.preventDefault(); 
                        myClientEvents.FireEvent("menu-item-clicked", $(this).attr("id"), null);

                    });
});


<div style="float: left;" class="menu">
 <a id="1" href="#">Menu Item1</a><br />
 <a id="2" href="#">Menu Item2</a><br />
 <a id="3" href="#">Menu Item3</a><br />
 <a id="4" href="#">Menu Item4</a><br />
</div>



// event subscriber control
$(document).ready(function() 
{    
    myClientEvents.AttachToEvent("menu-item-clicked", menuItemChanged);
    myClientEvents.AttachToEvent("menu-item-clicked", menuItemChanged2);
    myClientEvents.AttachToEvent("menu-item-clicked", menuItemChanged3);
});


function menuItemChanged(id, contextData) 
{
    alert('menuItemChanged ' + id);
}

function menuItemChanged2(id, contextData) 
{
    alert('menuItemChanged2 ' + id);
}

function menuItemChanged3(id, contextData) 
{
    alert('menuItemChanged3 ' + id);
}

2 个答案:

答案 0 :(得分:1)

jQuery的事件系统可以在trigger个事件时传递额外的处理程序参数。我们还通过创建将控件名称映射到选择器的注册表来将控制命名空间与jQuery选择器分开。为了在注册控件之前处理绑定到控件的处理程序,我们实现了绑定延迟机制。

var controls = {};
(function ControlRegistry(controls) {
  controls.items = {};
  function bindNow(selector, event, eventData, handler) {
      $(selector).bind(event, eventData, handler);
  }
  function delayBinding(queue, event, eventData, handler) {
      queue.push([event, eventData, handler]);
  }
  function bindAll(queue, selector) {
      for (var i=0; i<queue.length; ++i) {
          var args = queue[i];
          args.unshift(selector);
          bindNow.apply(controls, args);
      }
  }
  controls.register = function (name, selector) {
    if (typeof this.items[name] == 'object') {
       bindAll(this.items[name], selector);
    }
    this.items[name] = selector;
  };
  controls.bind = function (control, event, eventData, handler) {
    jQuery.isFunction( eventData ) {
        handler = eventData;
        eventData = null;
    }
    switch (typeof this.items[control]) {
      case 'undefined':
        this.items[control] = [];
        // FALLTHRU
      case 'object':
        delayBinding(this.items[control], event, eventData, handler);
        break;

      case 'string':
        bindNow(this.items[control], event, eventData, handler);
        break;
    }
  }
})(controls);

$(document).ready(function() 
{
    controls.register('menuItem', '.menu > a');
    $(".menu > a").click( function(event) 
                    {
                        $(this).trigger("menu-item-clicked", [$(this).attr("id"), 'cow', 'moo']);

                    });
});

其他地方:

function menuItemChanged(evt, id, animal, speech) 
{
    alert('menuItemChanged ' + id 
          + '\nThe ' + animal + ' says "' + speech + '."');
}

function menuItemChanged2(evt, id, animal, speech)) 
{
    alert('menuItemChanged2 ' + id 
          + '\nThe ' + animal + ' says "' + speech + '."');
}

function menuItemChanged3(evt, id, animal, speech)) 
{
    alert('menuItemChanged3 ' + id 
          + '\nThe ' + animal + ' says "' + speech + '."');
}

$(document).ready(function() 
{
    controls.bind('menuItem', "menu-item-clicked", menuItemChanged);
    controls.bind('menuItem', "menu-item-clicked", menuItemChanged2);
    controls.bind('menuItem', "menu-item-clicked", menuItemChanged3);
});

更新

如果您包含在处理程序绑定到其事件之前注册控件的限制,则控制注册表可以大大简化:

var controls = {
  register: function (name, selector) {
    if (typeof this[name] != 'function') {
      this[name] = selector;
    }
  };
};
...

controls.register('menuItem', '.menu > a');
$(document).ready(function() 
{
    $(".menu > a").click( function(event) 
                    {
                        $(this).trigger("menu-item-clicked", [$(this).attr("id"), 'cow', 'moo']);

                    });
});
...

$(document).ready(function() 
{
    $(controls.menuItem).bind("menu-item-clicked", menuItemChanged);
    $(controls.menuItem).bind("menu-item-clicked", menuItemChanged2);
    $(controls.menuItem).bind("menu-item-clicked", menuItemChanged3);
});

这是一个合理的限制,因为您可以提前注册(在控件的脚本中)并延迟绑定(在$(document).ready中)。

答案 1 :(得分:0)

我原来的解决方案最终成为了正确的解决方案,因为它以简单明了的方式实现了我所处的松散耦合。