mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
implement relatedTarget retargeting
This commit is contained in:
@@ -791,6 +791,33 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
keypress: true
|
||||
};
|
||||
|
||||
function pathComposer(startNode, composed) {
|
||||
var composedPath = [];
|
||||
var current = startNode;
|
||||
while (current) {
|
||||
composedPath.push(current);
|
||||
var contents = current.getDestinationInsertionPoints &&
|
||||
current.getDestinationInsertionPoints();
|
||||
if (contents && contents.length) {
|
||||
for (var i = 0; i < contents.length - 1; i++) {
|
||||
composedPath.push(contents[i]);
|
||||
}
|
||||
current = contents[contents.length - 1];
|
||||
} else if (current.assignedSlot) {
|
||||
current = current.assignedSlot;
|
||||
} else if (current.host && composed) {
|
||||
current = current.host;
|
||||
} else {
|
||||
current = current.parentNode;
|
||||
}
|
||||
}
|
||||
// event composedPath includes window when composed = true in most recent native implementations
|
||||
if (composedPath[composedPath.length - 1] === document) {
|
||||
composedPath.push(window);
|
||||
}
|
||||
return composedPath;
|
||||
}
|
||||
|
||||
var EventMixin = {
|
||||
|
||||
__patched: 'Event',
|
||||
@@ -804,30 +831,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
composedPath() {
|
||||
if (!this.__composedPath) {
|
||||
var composedPath = [];
|
||||
var current = this.__target;
|
||||
while (current) {
|
||||
composedPath.push(current);
|
||||
var contents = current.getDestinationInsertionPoints &&
|
||||
current.getDestinationInsertionPoints();
|
||||
if (contents && contents.length) {
|
||||
for (var i = 0; i < contents.length - 1; i++) {
|
||||
composedPath.push(contents[i]);
|
||||
}
|
||||
current = contents[contents.length - 1];
|
||||
} else if (current.assignedSlot) {
|
||||
current = current.assignedSlot;
|
||||
} else if (current.host && this.composed) {
|
||||
current = current.host;
|
||||
} else {
|
||||
current = current.parentNode;
|
||||
}
|
||||
}
|
||||
// event composedPath includes window when composed = true in most recent native implementations
|
||||
if (composedPath[composedPath.length - 1] === document) {
|
||||
composedPath.push(window);
|
||||
}
|
||||
this.__composedPath = composedPath;
|
||||
this.__composedPath = pathComposer(this.__target, this.composed);
|
||||
}
|
||||
return this.__composedPath;
|
||||
},
|
||||
@@ -846,6 +850,32 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
}
|
||||
},
|
||||
get relatedTarget() {
|
||||
if (!this.__relatedTarget) {
|
||||
return null;
|
||||
}
|
||||
if (!this.__relatedTargetComposedPath) {
|
||||
this.__relatedTargetComposedPath = pathComposer(this.__relatedTarget, true);
|
||||
}
|
||||
var path = this.__relatedTargetComposedPath;
|
||||
var currentRoot = this.target.getRootNode();
|
||||
var rt = null;
|
||||
var idx = path.indexOf(currentRoot);
|
||||
if (idx === -1) {
|
||||
var targetPath = this.composedPath();
|
||||
var tidx = targetPath.indexOf(currentRoot) + 1;
|
||||
while(idx === -1 && tidx < (targetPath.length - 1)) {
|
||||
idx = path.indexOf(targetPath[++tidx]);
|
||||
}
|
||||
} else {
|
||||
idx--;
|
||||
}
|
||||
rt = path[idx];
|
||||
while(idx >= 0 && rt && !rt.shadowRoot) {
|
||||
rt = path[--idx];
|
||||
}
|
||||
return rt;
|
||||
},
|
||||
stopPropagation() {
|
||||
Event.prototype.stopPropagation.call(this);
|
||||
this.__propagationStopped = true;
|
||||
@@ -916,7 +946,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
e.__proto__ = proto;
|
||||
}
|
||||
}
|
||||
if (e.composedPath().indexOf(this) > -1) {
|
||||
if (e.composedPath().indexOf(this) > -1 && e.target !== e.relatedTarget) {
|
||||
return fn(e);
|
||||
} else {
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
@@ -39,6 +39,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
'unit/case-map.html',
|
||||
'unit/attributes.html',
|
||||
'unit/async.html',
|
||||
'unit/shady-events.html'
|
||||
];
|
||||
|
||||
// http://eddmann.com/posts/cartesian-product-in-javascript/
|
||||
|
||||
40
test/smoke/relatedtarget.html
Normal file
40
test/smoke/relatedtarget.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!doctype html>
|
||||
<template id="a">
|
||||
<div>Hi</div>
|
||||
</template>
|
||||
<template id="b">
|
||||
<div>Hello</div>
|
||||
</template>
|
||||
<template id="c">
|
||||
<x-inner id="one"></x-inner>
|
||||
<x-inner id="two"></x-inner>
|
||||
</template>
|
||||
<x-outer id="outer"></x-outer>
|
||||
<x-other id="other"></x-other>
|
||||
<script>
|
||||
function shadowMaker(template) {
|
||||
return class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({mode: 'open'});
|
||||
this.shadowRoot.appendChild(document.importNode(template.content, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define('x-inner', shadowMaker(document.querySelector('#b')));
|
||||
customElements.define('x-other', shadowMaker(document.querySelector('#a')));
|
||||
customElements.define('x-outer', shadowMaker(document.querySelector('#c')));
|
||||
|
||||
function fire() {
|
||||
var inner = outer.shadowRoot.firstElementChild;
|
||||
var relatedTarget = outer.shadowRoot.querySelector('#two').shadowRoot.firstElementChild;
|
||||
// var target = other.shadowRoot.firstElementChild;
|
||||
var target = outer.shadowRoot.querySelector('#one');
|
||||
target.dispatchEvent(new MouseEvent('foo', {bubbles: true, composed: true, relatedTarget}));
|
||||
}
|
||||
|
||||
outer.shadowRoot.addEventListener('foo', e => {
|
||||
console.log(e.target, e.relatedTarget)
|
||||
console.log(e.composedPath());
|
||||
});
|
||||
</script>
|
||||
@@ -212,6 +212,26 @@ suite('ShadyDOM event patching', function() {
|
||||
assert.equal(el.events.length, 1);
|
||||
assert.equal(el.events[0], el.$.child);
|
||||
});
|
||||
|
||||
test('composed relatedTarget retargets', function() {
|
||||
var els = fixture('relatedtarget');
|
||||
var a = els[0];
|
||||
var b = els[1];
|
||||
var ev = new MouseEvent('foo', {bubbles: true, composed: true, relatedTarget: b.$.child});
|
||||
ev.__composed = true;
|
||||
a.$.child.dispatchEvent(ev);
|
||||
assert.property(a, 'event');
|
||||
assert.deepEqual(a.event, {target: a, relatedTarget: b});
|
||||
});
|
||||
|
||||
test('events do not fire if relatedtarget and target are the same node after retargeting', function() {
|
||||
var els = fixture('relatedtarget');
|
||||
var a = els[0];
|
||||
var ev = new MouseEvent('foo', {bubbles: true, composed: true, relatedTarget: a.$.child});
|
||||
ev.__composed = true;
|
||||
a.dispatchEvent(ev);
|
||||
assert.notProperty(a, 'event');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user