From: Dave Methvin Date: Thu, 13 Oct 2011 20:30:40 +0000 (-0400) Subject: Fix #10489. Disconnected elements don't bubble to document. X-Git-Tag: 1.7b2~1 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0a3cab8d4916045da51d843bdb6385c110ab3163;p=jquery.git Fix #10489. Disconnected elements don't bubble to document. --- diff --git a/src/event.js b/src/event.js index 17872f02d..a475a25ea 100644 --- a/src/event.js +++ b/src/event.js @@ -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; } diff --git a/test/unit/event.js b/test/unit/event.js index 91940a417..28df8ee39 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -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( - '

b

' - ).appendTo( "body" ); + '

b

' + ), + 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( "

hi

" ) + .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);