Skip to content
Permalink
Browse files

Fix #13180: don't delegate into SVG <use>

(cherry picked from commits 36457cb..f860e0b)
  • Loading branch information...
gibson042 committed Jan 14, 2013
1 parent 7767234 commit b75b9ef8d030b0dbf247e826bfbf9585c7dbf98b
Showing with 66 additions and 45 deletions.
  1. +47 −37 src/event.js
  2. +19 −8 test/unit/event.js
@@ -331,11 +331,10 @@ jQuery.event = {
// Make a writable jQuery.Event from the native event object
event = jQuery.event.fix( event );

var i, j, cur, ret, selMatch, matched, matches, handleObj, sel,
var i, j, ret, matched, handleObj,
handlerQueue = [],
args = core_slice.call( arguments ),
handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
delegateCount = handlers.delegateCount,
special = jQuery.event.special[ event.type ] || {};

// Use the fix-ed jQuery.Event rather than the (read-only) native event
@@ -347,41 +346,8 @@ jQuery.event = {
return;
}

// Determine handlers that should run if there are delegated events
// Avoid non-left-click bubbling in Firefox (#3861)
if ( delegateCount && !(event.button && event.type === "click") ) {

for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {

// Ignore clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764)
if ( cur.disabled !== true || event.type !== "click" ) {
selMatch = {};
matches = [];
i = 0;
for ( ; i < delegateCount; i++ ) {
handleObj = handlers[ i ];
sel = handleObj.selector;

if ( selMatch[ sel ] === undefined ) {
selMatch[ sel ] = handleObj.needsContext ?
jQuery( sel, this ).index( cur ) >= 0 :
jQuery.find( sel, this, null, [ cur ] ).length;
}
if ( selMatch[ sel ] ) {
matches.push( handleObj );
}
}
if ( matches.length ) {
handlerQueue.push({ elem: cur, handlers: matches });
}
}
}
}

// Add the remaining (directly-bound) handlers
if ( handlers.length > delegateCount ) {
handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
}
// Determine handlers
handlerQueue = jQuery.event.handlers.call( this, event, handlers );

// Run delegates first; they may want to stop propagation beneath us
i = 0;
@@ -419,6 +385,50 @@ jQuery.event = {
return event.result;
},

handlers: function( event, handlers ) {
var i, matches, sel, handleObj,
handlerQueue = [],
delegateCount = handlers.delegateCount,
cur = event.target;

// Find delegate handlers
// Black-hole SVG <use> instance trees (#13180)
// Avoid non-left-click bubbling in Firefox (#3861)
if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {

for ( ; cur != this; cur = cur.parentNode || this ) {

// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
if ( cur.disabled !== true || event.type !== "click" ) {
matches = [];
for ( i = 0; i < delegateCount; i++ ) {
handleObj = handlers[ i ];
sel = handleObj.selector;

if ( matches[ sel ] === undefined ) {
matches[ sel ] = handleObj.needsContext ?
jQuery( sel, this ).index( cur ) >= 0 :
jQuery.find( sel, this, null, [ cur ] ).length;
}
if ( matches[ sel ] ) {
matches.push( handleObj );
}
}
if ( matches.length ) {
handlerQueue.push({ elem: cur, handlers: matches });
}
}
}
}

// Add the remaining (directly-bound) handlers
if ( delegateCount < handlers.length ) {
handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
}

return handlerQueue;
},

// Includes some event props shared by KeyEvent and MouseEvent
props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),

@@ -1231,29 +1231,40 @@ test(".trigger() doesn't bubble load event (#10717)", function() {
jQuery( window ).off( "load" );
});

test("Delegated events in SVG (#10791)", function() {
test("Delegated events in SVG (#10791; #13180)", function() {
expect(2);

var svg = jQuery(
var e,
svg = jQuery(
"<svg height='1' version='1.1' width='1' xmlns='http://www.w3.org/2000/svg'>" +
"<defs><rect id='ref' x='10' y='20' width='100' height='60' r='10' rx='10' ry='10'></rect></defs>" +
"<rect class='svg-by-class' x='10' y='20' width='100' height='60' r='10' rx='10' ry='10'></rect>" +
"<rect id='svg-by-id' x='10' y='20' width='100' height='60' r='10' rx='10' ry='10'></rect>" +
"<use id='use' xlink:href='#ref'></use>" +
"</svg>"
).appendTo( "body" );
);

jQuery( "body" )
jQuery("#qunit-fixture")
.append( svg )
.on( "click", "#svg-by-id", function() {
ok( true, "delegated id selector" );
})
.on( "click", "[class~='svg-by-class']", function() {
ok( true, "delegated class selector" );
})
.find( "#svg-by-id, [class~='svg-by-class']" )
.trigger( "click" )
.end()
.off( "click" );
.trigger("click")
.end();

// Fire a native click on an SVGElementInstance (the instance tree of an SVG <use>)
// to confirm that it doesn't break our event delegation handling (#13180)
if ( document.createEvent ) {
e = document.createEvent("MouseEvents");
e.initEvent( "click", true, true );
svg.find("#use")[0].instanceRoot.dispatchEvent( e );
}

svg.remove();
jQuery("#qunit-fixture").off("click");
});

test("Delegated events in forms (#10844; #11145; #8165; #11382, #11764)", function() {

0 comments on commit b75b9ef

Please sign in to comment.
You can’t perform that action at this time.