Skip to content

Commit

Permalink
[BUGFIX beta] fix mouseEnter/Leave event delegation w/o jQuery for SV…
Browse files Browse the repository at this point in the history
…G elements in IE11

IE11 does not support `Node#contains()` on SVG elements, so in that case a custom fallback implementation is used

Fixes #17225
  • Loading branch information
simonihmig committed Nov 30, 2018
1 parent 84cf9c8 commit 82ee67f
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,62 @@ moduleFor(
assert.strictEqual(receivedLeaveEvents[0].target, outer);
}

['@test delegated event listeners work for mouseEnter on SVG elements'](assert) {
let receivedEnterEvents = [];
let receivedLeaveEvents = [];

this.registerComponent('x-foo', {
ComponentClass: Component.extend({
tagName: 'svg',
mouseEnter(event) {
receivedEnterEvents.push(event);
},
mouseLeave(event) {
receivedLeaveEvents.push(event);
},
}),
template: `<g id="inner"></g>`,
});

this.render(`{{x-foo id="outer"}}`);

let parent = this.element;
let outer = this.$('#outer')[0];
let inner = this.$('#inner')[0];

// mouse moves over #outer
this.runTask(() => {
this.$(outer).trigger('mouseenter', { canBubble: false, relatedTarget: parent });
this.$(outer).trigger('mouseover', { relatedTarget: parent });
this.$(parent).trigger('mouseout', { relatedTarget: outer });
});
assert.equal(receivedEnterEvents.length, 1, 'mouseenter event was triggered');
assert.strictEqual(receivedEnterEvents[0].target, outer);

// mouse moves over #inner
this.runTask(() => {
this.$(inner).trigger('mouseover', { relatedTarget: outer });
this.$(outer).trigger('mouseout', { relatedTarget: inner });
});
assert.equal(receivedEnterEvents.length, 1, 'mouseenter event was not triggered again');

// mouse moves out of #inner
this.runTask(() => {
this.$(inner).trigger('mouseout', { relatedTarget: outer });
this.$(outer).trigger('mouseover', { relatedTarget: inner });
});
assert.equal(receivedLeaveEvents.length, 0, 'mouseleave event was not triggered');

// mouse moves out of #outer
this.runTask(() => {
this.$(outer).trigger('mouseleave', { canBubble: false, relatedTarget: parent });
this.$(outer).trigger('mouseout', { relatedTarget: parent });
this.$(parent).trigger('mouseover', { relatedTarget: outer });
});
assert.equal(receivedLeaveEvents.length, 1, 'mouseleave event was triggered');
assert.strictEqual(receivedLeaveEvents[0].target, outer);
}

['@test delegated event listeners work for mouseEnter/Leave with skipped events'](assert) {
let receivedEnterEvents = [];
let receivedLeaveEvents = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import jQuery, { jQueryDisabled } from './jquery';
import ActionManager from './action_manager';
import fallbackViewRegistry from '../compat/fallback-view-registry';
import addJQueryEventDeprecation from './jquery_event_deprecation';
import { contains } from './utils';

/**
@module ember
Expand Down Expand Up @@ -335,7 +336,9 @@ export default EmberObject.extend({
while (
target &&
target.nodeType === 1 &&
(!related || (related !== target && !target.contains(related)))
(related === null ||
(related !== target &&
!contains(target, related)))
) {
// mouseEnter/Leave don't bubble, so there is no logic to prevent it as with other events
if (viewRegistry[target.id]) {
Expand Down
12 changes: 12 additions & 0 deletions packages/@ember/-internals/views/lib/system/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,15 @@ export const elMatches =
export function matches(el, selector) {
return elMatches.call(el, selector);
}

export function contains(a, b) {
if (a.contains !== undefined) {
return a.contains(b);
}
while ((b = b.parentNode)) {
if (b === a) {
return true;
}
}
return false;
}

0 comments on commit 82ee67f

Please sign in to comment.