Skip to content

Commit

Permalink
Fix #10489. Disconnected elements don't bubble to document.
Browse files Browse the repository at this point in the history
  • Loading branch information
dmethvin committed Oct 13, 2011
1 parent 7bef99e commit 0a3cab8
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 36 deletions.
50 changes: 27 additions & 23 deletions src/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -310,14 +300,14 @@ 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
if ( !elem ) {

// 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 );
Expand All @@ -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;
}
Expand Down
51 changes: 38 additions & 13 deletions test/unit/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 0a3cab8

Please sign in to comment.