]> source.dussan.org Git - jquery.git/commitdiff
Fix #10489. Disconnected elements don't bubble to document.
authorDave Methvin <dave.methvin@gmail.com>
Thu, 13 Oct 2011 20:30:40 +0000 (16:30 -0400)
committerDave Methvin <dave.methvin@gmail.com>
Thu, 13 Oct 2011 20:30:40 +0000 (16:30 -0400)
src/event.js
test/unit/event.js

index 17872f02dfbbbf1965fea10c2fd2e0b7c2c83786..a475a25eae38b3f5e967341912832acf2504e043 100644 (file)
@@ -261,17 +261,7 @@ jQuery.event = {
                // Event object or event type
                var type = event.type || event,
                        namespaces = [],
-                       cache, exclusive, i, cur, old, ontype, special, doc, eventPath, bubbleType,
-                       addHandlers = function( elem, type ) {
-                               // Defer getting handler so we don't waste time in case propagation is stopped
-                               if ( (jQuery._data( elem, "events" ) || {})[ type ] ) {
-                                       eventPath.push({ elem: elem, type: type /*, handler: jQuery._data( elem, "handle" ) */ });
-                               }
-                               // IE doesn't like method names with a colon (#3533, #8272)
-                               if ( ontype && jQuery.acceptData( elem ) && elem[ ontype ] ) {
-                                       eventPath.push({ elem: elem, type: type, handler: elem[ ontype ] });
-                               }
-                       };
+                       cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
 
                if ( type.indexOf( "!" ) >= 0 ) {
                        // Exclusive events trigger only for the exact event (no namespaces)
@@ -310,7 +300,6 @@ jQuery.event = {
                // triggerHandler() and global events don't bubble or run the default action
                if ( onlyHandlers || !elem ) {
                        event.preventDefault();
-                       event.stopPropagation();
                }
 
                // Handle a global trigger
@@ -318,6 +307,7 @@ jQuery.event = {
 
                        // TODO: Stop taunting the data cache; remove global events and always attach to document
                        cache = jQuery.cache;
+                       event.stopPropagation();
                        for ( i in cache ) {
                                if ( cache[ i ].events && cache[ i ].events[ type ] ) {
                                        jQuery.event.trigger( event, data, cache[ i ].handle.elem );
@@ -344,23 +334,37 @@ jQuery.event = {
 
                // Determine event propagation path in advance, per W3C events spec (#9951)
                // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
-               // Always fire handlers for the target, even if prop is stopped in advance
-               eventPath = [];
-               addHandlers( elem, special.bindType || type );
-               doc = elem.ownerDocument;
-               if ( doc && !special.noBubble && !jQuery.isWindow( elem ) & !event.isPropagationStopped() ) {
+               eventPath = [[ elem, special.bindType || type ]];
+               if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
                        bubbleType = special.delegateType || type;
+                       old = null;
                        for ( cur = elem.parentNode; cur; cur = cur.parentNode ) {
-                               addHandlers( cur, bubbleType );
+                               eventPath.push([ cur, bubbleType ]);
+                               old = cur;
+                       }
+
+                       // Only add window if we got to document (e.g., not plain obj or detached DOM)
+                       if ( old && old === elem.ownerDocument ) {
+                               eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
                        }
-                       addHandlers( doc.defaultView || doc.parentWindow || window, bubbleType );
                }
 
-               // Bubble up the DOM tree
+               // Fire handlers on the event path
                for ( i = 0; i < eventPath.length; i++ ) {
-                       cur = eventPath[ i ];
-                       event.type = cur.type;
-                       ( cur.handler || jQuery._data( cur.elem, "handle" ) ).apply( cur.elem, data );
+
+                       cur = eventPath[i][0];
+                       event.type = eventPath[i][1];
+
+                       handle = (jQuery._data( cur, "events" ) || {})[ event.type ] && jQuery._data( cur, "handle" );
+                       if ( handle ) {
+                               handle.apply( cur, data );
+                       }
+                       handle = ontype && cur[ ontype ];
+                       if ( handle && jQuery.acceptData( cur ) ) {
+                               handle.apply( cur, data );
+                       }
+
                        if ( event.isPropagationStopped() ) {
                                break;
                        }
index 91940a41770680fdf1d96c1ebd9856f944e6e281..28df8ee39ff9da792cea6aee0b5dfc5f9250bb65 100644 (file)
@@ -34,30 +34,31 @@ test("bind(),live(),delegate() with non-null,defined data", function() {
 
 });
 
-/*
-Removed because Chrome 13 snaps/crashes on this 2011-09-07
-
 test("Handler changes and .trigger() order", function() {
     expect(1);
 
     var markup = jQuery(
-        '<div><p><b class="a">b</b></p></div>'
-    ).appendTo( "body" );
+        '<div><div><p><span><b class="a">b</b></span></p></div></div>'
+               ),
+               path = "";
 
-    var path = "";
-    jQuery( "b" ).parents().bind( "click", function(e){
-        path += this.nodeName.toLowerCase() + " ";
-        // Should not change the event triggering order
-        $(this).parent().remove();
-    });
+    markup
+               .find( "*" ).andSelf().on( "click", function( e ) {
+                       path += this.nodeName.toLowerCase() + " ";
+               })
+               .filter( "b" ).on( "click", function( e ) {
+                       // Removing span should not stop propagation to original parents
+                       if ( e.target === this ) {
+                               jQuery(this).parent().remove();
+                       }
+               });
 
     markup.find( "b" ).trigger( "click" );
 
-    equals( path, "p div body html ", "Delivered all events" )
+    equals( path, "b p div div ", "Delivered all events" );
 
     markup.remove();
 });
-*/
 
 test("bind(), with data", function() {
        expect(4);
@@ -1110,6 +1111,30 @@ test("trigger(eventObject, [data], [fn])", function() {
        $parent.unbind().remove();
 });
 
+test(".trigger() bubbling on disconnected elements (#10489)", function() {
+       expect(2);
+
+       jQuery( window ).on( "click", function(){
+               ok( false, "click fired on window" );
+       });
+
+       jQuery( "<div><p>hi</p></div>" )
+               .on( "click", function() {
+                       ok( true, "click fired on div" );
+               })
+               .find( "p" )
+                       .on( "click", function() { 
+                               ok( true, "click fired on p" );
+                       })
+                       .click()
+                       .off( "click" )
+               .end()
+               .off( "click" )
+               .remove();
+
+       jQuery( window ).off( "click" );
+});
+
 test("jQuery.Event( type, props )", function() {
 
        expect(5);