【问题标题】:Javascript Delegation Best Practice [closed]Javascript委派最佳实践[关闭]
【发布时间】:2014-01-09 12:15:51
【问题描述】:

我现在正在开发/已经开发了一些系统,我在编程中遇到了一个怪癖,我似乎将 所有我的 JQuery 函数委托给文档,如下所示:

$(document).on('click', '.modal-editor', function () {
    //code
});

$(document).on('click', '.another-class', function () {
    //code
});

$(document).on('click', '#another-id', function () {
    //code
});

这有什么问题吗?

更新:

好的,所以在本质上这没有什么问题,直到:

  1. 应用程序达到了需要更多本地化委派以防止 UI 变慢的规模。 “发生的每个事件都需要针对 e.target 和文档之间的每个元素运行每个选择器”

  2. 如果使用嵌套函数,这样的委托会增加传播等意外行为的可能性。

回到我的问题(最佳实践),最佳实践是将事件委托给最接近的静态元素,以尽量避免上述情况。

【问题讨论】:

  • 我不这么认为...你应该没事...
  • 这可能会成为一个大规模的问题;没有一概而论的答案。
  • 关于您的更新,不,我不会等待出现性能问题。 jQuery 摆脱了 .live() 方法是有原因的。对于明智的做法,即使目前没有明显的好处,也有一些话要说。
  • @cookiemonster - jQuery 摆脱了 live() 方法,因为它的功能过于具体。 jQuery API 倾向于使用相同的方法执行多项操作,具体取决于您提供的参数。您可以使用on()delegate() 执行与live() 相同的功能以及更多功能。他们没有摆脱它,因为它不能正常工作或被滥用。
  • @Adam:除了它滥用了。人们不知道它是如何工作的,所以他们只会使用它绑定所有元素。是的,它太具体了,但它也不能描述实际发生的事情,所以人们最终会误用它。

标签: javascript jquery delegation


【解决方案1】:

“这有什么问题吗?”

是的。每个发生的事件都需要针对e.targetdocument 之间的每个元素运行每个选择器。这太贵了。

基本上就像这个简化的伪代码:

// start on the e.target
var node = e.target;

// For the target and all its ancestors until the bound element...
while (node && node !== this) {

    // ...test the current node against every click selector it was given
    for (var i = 0; i < click_selectors.length; i++) {

        // If a selector matches...
        if ($(node).is(selector)) {
            // ...run the handler for that selector
        }
    }
    node = node.parentNode;
}

此外,如果您的代码或您加载的其他代码在e.targetdocument 之间绑定了一个处理程序,并调用e.stopPropagation(),则事件将永远不会到达document,因此您的事件不会触发.

让您的委托尽可能接近预期元素会更好。并且主要将它用于动态加载的元素,因为它会增加额外的开销,尤其是在事件快速触发的情况下。

【讨论】:

    【解决方案2】:

    是的。本身并没有,实际上,但是从document(DOM 的根)委派所有事件确实意味着 所有 点击事件,包括您不感兴趣的点击事件将被处理至少部分:

    $(document).on('click', '$another-id', function(){});
    

    在这方面是一个特别糟糕的主意:你在单个元素之后,但是如果我单击正文中的任何位置,jQ 会执行以下操作:

    if (event.target.id == 'another-id')
    {
        return callback.call(event.target, $(event));//where callback is the anonymous function you passed to on
    }
    return event;
    

    所以所有点击事件都会导致函数调用。这会降低您的 UI 速度。
    绝不应该停止委派事件,而是明智地绑定侦听器。例如,如果您要使用的所有 DOM 元素都包含在 #container div 中,则将您的侦听器绑定在那里。如果要处理导航事件,请将侦听器绑定到包含您之后的所有导航元素的节点
    此外,如果您取消了一个事件,但 stopPropagation 失败,该事件仍将最终调用所有其他可能排队的侦听器。 Returnign false 也可能造成麻烦,因为在 jQ 中,return false 被转换为 e.preventDefault(); e.stopPropagation()。所以在处理嵌套元素时要小心,如果你想处理页面中链接的点击,以及像&lt;a class='navigation'&gt;这样的元素,可能会调用这两个处理程序,具体取决于使用的选择器:

    $(document).on('click', 'a', function(){});//is called for all links
    $(document).on('click', 'a.navigation', function(){});//is called for navigation
    

    首先调用哪个?在给定的情况下,您想要使用哪个?这里有错误的余地,不应该存在:

    $('#navContainer').on('click', 'a.navigation', function(){});//is called for all links
    

    至少也让事情变得更安全、更清晰、更轻松。

    如果你想委托一个事件,使用一个 ID 选择器,并且元素已经存在于 DOM 中,不要委托

    $('#another-id').on('click', function(){});
    

    更短,甚至可能更快

    【讨论】:

    • 这是一个公平的观点,所以我不仅预期会减慢 UI 的速度,而且还会增加冒泡的机会吗?
    • @Edward:我已经编辑了更多内容,但基本上是的:您陷入了事件循环,可能会减慢 UI,并且没有 确定 哪个事件处理程序将首先被调用,因此您无法知道哪个回调将对哪个元素上的哪个事件做出反应,尤其是对于冒泡可能导致问题的嵌套元素
    【解决方案3】:

    如果您希望将特定事件应用于多个元素,而不需要每个元素都有自己的事件处理程序,则委托非常有用。在上面的例子中,这可能是前两种方法。

    理论上只有一个元素与标准匹配,它可能会影响性能,具体取决于文档的大小,因为每个事件都必须针对处理程序过滤器进行测试以查看它是否匹配.

    此外,如果每个方法都作为委托添加,并且您经常加载和上传页面的各个部分,那么这些事件的停留时间将比它们所属页面上的元素更长。

    您可以将处理程序委托给文档元素以外的其他内容,例如周围的div 或在这种情况下可能的其他内容。或者使用事件命名空间来确保事件不会被多次添加。

    【讨论】:

      【解决方案4】:

      你的代码没有问题,

      $(document) 不必总是放在事件委托中,该选择器指的是您的元素被动态添加到的最近的父元素:

      语法:

      $(closest parent selector).on('event','target selector', function(){
      });
      

      你也可以使用document.body

      $(document.body).on('event','target selecor',function(){
      });
      

      旁注:对于现有的 DOM 元素,您可以使用正常的点击事件。

      $('selector').click(function(){
      });
      

      $('selector').on('click',function(){
      });
      

      有关更多信息,请参阅.on() API 文档。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-02
        相关资源
        最近更新 更多