阻止事件冒泡和使用live()方法。我失去了什么?

6

我已经阅读了jQuery文档,我知道如果我使用live()将事件绑定到元素上,使用stopPropagation()没有任何意义,因为live()绑定到文档节点的事件已经传播了。所以,我想下面的代码会触发3次警报,因为stopPropagation不会停止任何东西:

<ul style="border:red solid 1px;">
    <li style="padding:10px; border:blue solid 1px;">
        <p style="border:green solid 1px;"><a href="#">link</a></p>
    </li>
    <li style="padding:10px; border:blue solid 1px;">
        <p style="border:green solid 1px;">no link</p>
    </li>
</ul>

<script type="text/javascript">
    $('a').live( "click", function( e ) {alert("link");e.stopPropagation()} );
    $('ul').live( "click", function( e ) {alert("ul");} );
    $('ul li').live( "click", function( e ) {alert("li");} );
</script>

当我点击链接时,我期望出现“li”和“ul”的警报,然而stopPropagation却阻止了其他事件的执行,尽管它本应该是无用的。 我漏掉了什么?

为什么不直接使用 $('a').on( "click", function() {alert("link"); return false;} ); - Morpheus
如果我点击精确链接,您的代码按预期工作。我看不到ul和li警报。如果我点击实际链接旁边的空格,我会看到另外两个警报。您可能希望为元素设置宽度,以便不会出现此行为。 - Ravi Y
似乎是因为对于ul和ul li,您也使用了live。如果将它们更改为bind,它们都会被调用。 - Andy
1个回答

6
根据文档stopPropagation()live()中无法按预期工作,因为live()仅在事件冒泡到document后才执行该事件。
但是,如果您使用的是jQuery 1.4.3或更高版本,则stopPropagation()开始起作用。
自1.4.3以来,live()已经重写了多次。
live()文档列出了许多原因,说明为什么使用其他绑定方法比较好。不过,文档中的信息似乎是针对1.4.1行为的,并且与实际当前行为不完全同步。
例如,查看添加了on()的1.7.1源代码,显示live()正在使用on():
live: function( types, data, fn ) {
    jQuery( this.context ).on( types, this.selector, data, fn );
    return this;
}

演示 - 使用您的代码并在jQuery 1.4.1中运行-stopPropagation不起作用,如预期所需。

演示 - 使用您的代码并在jQuery 1.4.3中运行-stopPropagation现在可以工作。

摘要

live()在1.4.3中进行了重新编写。我认为这是由于添加了delegate()
随着每个jQuery版本的改进,live()一直在不断更新。

一般来说,为了避免使用live()时出现任何意外的结果,请遵循文档中的指南,并使用给定版本的建议方法:

$(selector).live(events, data, handler);                // jQuery 1.3+
$(document).delegate(selector, events, data, handler);  // jQuery 1.4.3+
$(document).on(events, selector, data, handler);        // jQuery 1.7+

为了完整性,我在下面添加了1.4.1和1.4.3版本的live()源代码提取。

1.4.1源码

live: {
    add: function(proxy, data, namespaces, live) {
        jQuery.extend(proxy, data || {});

        proxy.guid += data.selector + data.live;
        data.liveProxy = proxy;

        jQuery.event.add(this, data.live, liveHandler, data);

    },

    remove: function(namespaces) {
        if (namespaces.length) {
            var remove = 0,
                name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");

            jQuery.each((jQuery.data(this, "events").live || {}), function() {
                if (name.test(this.type)) {
                    remove++;
                }
            });

            if (remove < 1) {
                jQuery.event.remove(this, namespaces[0], liveHandler);
            }
        }
    },
    special: {}
}

1.4.3 source

live: {
    add: function(handleObj) {
        jQuery.event.add(this, liveConvert(handleObj.origType, handleObj.selector), jQuery.extend({}, handleObj, {
            handler: liveHandler,
            guid: handleObj.handler.guid
        }));
    },

    remove: function(handleObj) {
        jQuery.event.remove(this, liveConvert(handleObj.origType, handleObj.selector), handleObj);
    }
}

它在1.4.3中无法工作,无论如何我都投了反对票,因为你帖子的90%都是误导性的,即“live”与委派不同。 - Esailija
@Esailija:我已经添加了演示,展示它在1.4.1中的工作情况,并加上了关于1.4.3中更改的注释。 在1.4.3中,live()被重写了,很可能是因为当时添加了delegate()live()是在1.4.1中添加的,在那个版本中stopPropagation()没有按预期工作。在1.4.3中,delegate()被添加以取代live()。 我认为这就是为什么如果有改进可用,jQuery live()会在每次发布时被重新编写的原因。为了方便起见,我还添加了1.4.1和1.4.3中live()的源代码。 - Nope
这种行为仍然很奇怪和不直观,它们都绑定在“document”上,并且只能在“document”上处理,只有e.stopImmediatePropagation才应该起作用。例如,这应该是等效的,但实际上并不是。http://jsfiddle.net/PHvVh/ - Esailija
@Esailija:我完全同意。这很奇怪。在live()的文档中,我也找不到关于版本更迭中live()的变化的任何信息,这使得文档中提到的一些关于live()不起作用的要点变得多余和令人困惑。 - Nope
感谢所有的回复。我以为可能是我漏掉了什么,但事实证明,根据jQuery版本的不同,同一个方法的工作方式也会有所不同。 - Barleby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接