mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
Fix #13180: don't delegate into SVG <use>
(cherry picked from commits 36457cb6afc12d4a755cf93442a502783a669517..f860e0bd2f7dd228a14704d78ed5208cbe870d01)
This commit is contained in:
parent
7767234364
commit
b75b9ef8d0
84
src/event.js
84
src/event.js
@ -331,11 +331,10 @@ jQuery.event = {
|
|||||||
// Make a writable jQuery.Event from the native event object
|
// Make a writable jQuery.Event from the native event object
|
||||||
event = jQuery.event.fix( event );
|
event = jQuery.event.fix( event );
|
||||||
|
|
||||||
var i, j, cur, ret, selMatch, matched, matches, handleObj, sel,
|
var i, j, ret, matched, handleObj,
|
||||||
handlerQueue = [],
|
handlerQueue = [],
|
||||||
args = core_slice.call( arguments ),
|
args = core_slice.call( arguments ),
|
||||||
handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
|
handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
|
||||||
delegateCount = handlers.delegateCount,
|
|
||||||
special = jQuery.event.special[ event.type ] || {};
|
special = jQuery.event.special[ event.type ] || {};
|
||||||
|
|
||||||
// Use the fix-ed jQuery.Event rather than the (read-only) native event
|
// Use the fix-ed jQuery.Event rather than the (read-only) native event
|
||||||
@ -347,41 +346,8 @@ jQuery.event = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine handlers that should run if there are delegated events
|
// Determine handlers
|
||||||
// Avoid non-left-click bubbling in Firefox (#3861)
|
handlerQueue = jQuery.event.handlers.call( this, event, handlers );
|
||||||
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 ) });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run delegates first; they may want to stop propagation beneath us
|
// Run delegates first; they may want to stop propagation beneath us
|
||||||
i = 0;
|
i = 0;
|
||||||
@ -419,6 +385,50 @@ jQuery.event = {
|
|||||||
return event.result;
|
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
|
// Includes some event props shared by KeyEvent and MouseEvent
|
||||||
props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
|
props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
|
||||||
|
|
||||||
|
@ -1231,17 +1231,21 @@ test(".trigger() doesn't bubble load event (#10717)", function() {
|
|||||||
jQuery( window ).off( "load" );
|
jQuery( window ).off( "load" );
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Delegated events in SVG (#10791)", function() {
|
test("Delegated events in SVG (#10791; #13180)", function() {
|
||||||
expect(2);
|
expect(2);
|
||||||
|
|
||||||
var svg = jQuery(
|
var e,
|
||||||
|
svg = jQuery(
|
||||||
"<svg height='1' version='1.1' width='1' xmlns='http://www.w3.org/2000/svg'>" +
|
"<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 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>" +
|
"<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>"
|
"</svg>"
|
||||||
).appendTo( "body" );
|
);
|
||||||
|
|
||||||
jQuery( "body" )
|
jQuery("#qunit-fixture")
|
||||||
|
.append( svg )
|
||||||
.on( "click", "#svg-by-id", function() {
|
.on( "click", "#svg-by-id", function() {
|
||||||
ok( true, "delegated id selector" );
|
ok( true, "delegated id selector" );
|
||||||
})
|
})
|
||||||
@ -1249,11 +1253,18 @@ test("Delegated events in SVG (#10791)", function() {
|
|||||||
ok( true, "delegated class selector" );
|
ok( true, "delegated class selector" );
|
||||||
})
|
})
|
||||||
.find( "#svg-by-id, [class~='svg-by-class']" )
|
.find( "#svg-by-id, [class~='svg-by-class']" )
|
||||||
.trigger( "click" )
|
.trigger("click")
|
||||||
.end()
|
.end();
|
||||||
.off( "click" );
|
|
||||||
|
|
||||||
svg.remove();
|
// 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery("#qunit-fixture").off("click");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Delegated events in forms (#10844; #11145; #8165; #11382, #11764)", function() {
|
test("Delegated events in forms (#10844; #11145; #8165; #11382, #11764)", function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user