mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
Make focus and blur events retarget and fire on hosts
This commit is contained in:
@@ -845,6 +845,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return ancestor;
|
||||
}
|
||||
}
|
||||
},
|
||||
stopPropagation() {
|
||||
Event.prototype.stopPropagation.call(this);
|
||||
this.__propagationStopped = true;
|
||||
},
|
||||
stopImmediatePropagation() {
|
||||
Event.prototype.stopImmediatePropagation.call(this);
|
||||
this.__propagationStopped = true;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -868,7 +876,33 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
ShadyDom.PatchedCustomEvent = mixinComposedFlag(CustomEvent);
|
||||
|
||||
var nonBubblingEventsToRetarget = {
|
||||
focus: true,
|
||||
blur: true
|
||||
};
|
||||
|
||||
function retargetNonBubblingEvent(e) {
|
||||
var path = e.composedPath();
|
||||
for (var i = 0, n; i < path.length; i++) {
|
||||
n = path[i];
|
||||
var hs = n.__handlers && n.__handlers[e.type];
|
||||
if (hs) {
|
||||
for (var j = 0, fn; (fn = hs[j]); j++) {
|
||||
// override `currentTarget` to let patched `target` calculate correctly
|
||||
Object.defineProperty(e, 'currentTarget', {value: n, configurable: true});
|
||||
fn.call(n, e);
|
||||
if (e.__propagationStopped) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShadyDom.addEventListener = function(type, fn, optionsOrCapture) {
|
||||
if (!fn) {
|
||||
return;
|
||||
}
|
||||
if (!this.__eventListenerCount) {
|
||||
this.__eventListenerCount = 0;
|
||||
}
|
||||
@@ -889,18 +923,58 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
}
|
||||
fn.__eventWrapper = wrappedFn;
|
||||
if (nonBubblingEventsToRetarget[type]) {
|
||||
this.__handlers = this.__handlers || {};
|
||||
this.__handlers[type] = this.__handlers[type] || [];
|
||||
this.__handlers[type].push(wrappedFn);
|
||||
}
|
||||
return origAddEventListener.call(this, type, wrappedFn, optionsOrCapture);
|
||||
};
|
||||
|
||||
ShadyDom.removeEventListener = function(type, fn, optionsOrCapture) {
|
||||
if (!fn) {
|
||||
return;
|
||||
}
|
||||
var wrapper = fn.__eventWrapper;
|
||||
origRemoveEventListener.call(this, type, wrapper || fn, optionsOrCapture);
|
||||
if (wrapper) {
|
||||
fn.__eventWrapper = null;
|
||||
this.__eventListenerCount--;
|
||||
if (nonBubblingEventsToRetarget[type]) {
|
||||
if (this.__handlers) {
|
||||
if (this.__handlers[type]) {
|
||||
var idx = this.__handlers[type].indexOf(wrapper);
|
||||
if (idx > -1) {
|
||||
this.__handlers[type].splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('focus', function(e){
|
||||
var proto = ShadyDom.patchImpl.prototypeForObject(e);
|
||||
if (!e.__target) {
|
||||
e.__target = e.target;
|
||||
e.__relatedTarget = e.__relatedTarget;
|
||||
e.__proto__ = proto;
|
||||
retargetNonBubblingEvent(e);
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
}, true);
|
||||
|
||||
document.addEventListener('blur', function(e){
|
||||
var proto = ShadyDom.patchImpl.prototypeForObject(e);
|
||||
if (!e.__target) {
|
||||
e.__target = e.target;
|
||||
e.__relatedTarget = e.__relatedTarget;
|
||||
e.__proto__ = proto;
|
||||
retargetNonBubblingEvent(e);
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
}, true);
|
||||
|
||||
ShadyDom.Mixins = {
|
||||
|
||||
Node: ShadyDom.extendAll({__patched: 'Node'}, NodeMixin),
|
||||
|
||||
@@ -19,53 +19,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<dom-module id="x-event-patched">
|
||||
<template>
|
||||
<div>
|
||||
<div id="candidate" on-foo="fooHandler"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
HTMLImports.whenReady(function() {
|
||||
Polymer({
|
||||
properties: {
|
||||
events: {
|
||||
type: Object,
|
||||
value: function() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
listeners: {
|
||||
"foo": "hostFooHandler"
|
||||
},
|
||||
is: 'x-event-patched',
|
||||
connected: function() {
|
||||
this.listen(this.shadowRoot, "rootFooHandler");
|
||||
},
|
||||
fooHandler: function(e) {
|
||||
this.events.div = {
|
||||
path: e.composedPath(),
|
||||
target: e.target
|
||||
};
|
||||
},
|
||||
hostFooHandler: function(e) {
|
||||
this.events.host = {
|
||||
path: e.composedPath && e.composedPath(),
|
||||
target: e.target
|
||||
};
|
||||
},
|
||||
rootFooHandler: function(e) {
|
||||
this.events.root = {
|
||||
path: e.composedPath && e.composedPath(),
|
||||
target: e.target
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="x-event-scoped">
|
||||
<template>
|
||||
<div id="scoped" on-composed="childHandler" on-scoped="childHandler"></div>
|
||||
@@ -93,25 +46,92 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
'scoped': 'hostHandler'
|
||||
},
|
||||
hostHandler: function(e) {
|
||||
this.hostEvents.push(e.type);
|
||||
this.hostEvents.push({
|
||||
target: e.target,
|
||||
type: e.type,
|
||||
path: e.composedPath()
|
||||
});
|
||||
},
|
||||
childHandler: function(e) {
|
||||
this.childEvents.push(e.type);
|
||||
this.childEvents.push({
|
||||
target: e.target,
|
||||
type: e.type,
|
||||
path: e.composedPath()
|
||||
});
|
||||
},
|
||||
fireComposed: function() {
|
||||
this.fire('composed', null, {node: this.$.scoped});
|
||||
return this.fire('composed', null, {node: this.$.scoped});
|
||||
},
|
||||
fireScoped: function(){
|
||||
this.fire('scoped', null, {node: this.$.scoped, composed: false});
|
||||
return this.fire('scoped', null, {node: this.$.scoped, composed: false});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<test-fixture id="patch">
|
||||
<dom-module id="x-focus">
|
||||
<template>
|
||||
<x-event-patched></x-event-patched>
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<div id="child" on-focus="focusHandler"></div>
|
||||
</template>
|
||||
<script>
|
||||
HTMLImports.whenReady(function() {
|
||||
Polymer({
|
||||
is: 'x-focus',
|
||||
properties: {
|
||||
events: {
|
||||
type: Array,
|
||||
value: function() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
listeners: {
|
||||
focus: 'focusHandler'
|
||||
},
|
||||
focusHandler: function(e) {
|
||||
this.events.push(e.target);
|
||||
},
|
||||
fireComposed: function() {
|
||||
var ev = new Event('focus', {composed: true});
|
||||
this.$.child.dispatchEvent(ev);
|
||||
},
|
||||
fireScoped: function() {
|
||||
var ev = new Event('focus');
|
||||
this.$.child.dispatchEvent(ev);
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="x-a">
|
||||
<template>
|
||||
<div id="child"></div>
|
||||
</template>
|
||||
<script>
|
||||
HTMLImports.whenReady(function() {
|
||||
Polymer({
|
||||
is: 'x-a',
|
||||
listeners: {
|
||||
'foo': 'fooHandler'
|
||||
},
|
||||
fooHandler: function(e) {
|
||||
this.event = {target: e.target, relatedTarget: e.relatedTarget};
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<test-fixture id="scoped">
|
||||
<template>
|
||||
<x-event-scoped></x-event-scoped>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
@@ -121,26 +141,33 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="scoping">
|
||||
<test-fixture id="focus">
|
||||
<template>
|
||||
<x-event-scoped></x-event-scoped>
|
||||
<x-focus></x-focus>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="relatedtarget">
|
||||
<template>
|
||||
<x-a id="one"></x-a>
|
||||
<x-a id="two"></x-a>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('ShadyDOM event patching', function() {
|
||||
|
||||
test('events retarget', function() {
|
||||
var el = fixture('scoped');
|
||||
el.fireComposed();
|
||||
assert.equal(el.hostEvents[0].target, el);
|
||||
assert.equal(el.childEvents[0].target, el.$.scoped);
|
||||
});
|
||||
|
||||
test('event.composedPath is consistent', function() {
|
||||
var el = fixture('patch');
|
||||
var e = new Event('foo', {bubbles: true, composed: true})
|
||||
el.$.candidate.dispatchEvent(e);
|
||||
assert.property(el.events, 'div');
|
||||
assert.equal(el.events.div.target, el.$.candidate);
|
||||
assert.property(el.events, 'host');
|
||||
assert.equal(el.events.host.path, el.events.div.path);
|
||||
assert.equal(el.events.host.target, el);
|
||||
// assert.property(el.events, 'root');
|
||||
// assert.equal(el.events.root.path, el.events.div.path);
|
||||
// assert.equal(el.events.root.target, el.$.candidate);
|
||||
var el = fixture('scoped');
|
||||
el.fireComposed();
|
||||
assert.equal(el.hostEvents.length, el.childEvents.length);
|
||||
});
|
||||
|
||||
test('event patching works on non Polymer elements', function() {
|
||||
@@ -155,14 +182,35 @@ suite('ShadyDOM event patching', function() {
|
||||
});
|
||||
|
||||
test('events handle the `composed` flag correctly', function() {
|
||||
var el = fixture('scoping');
|
||||
var el = fixture('scoped');
|
||||
el.fireScoped();
|
||||
el.fireComposed();
|
||||
assert.equal(el.hostEvents.length, 1);
|
||||
assert.equal(el.hostEvents[0], 'composed');
|
||||
assert.equal(el.hostEvents[0].type, 'composed');
|
||||
assert.equal(el.childEvents.length, 2);
|
||||
assert.equal(el.childEvents[0], 'scoped');
|
||||
assert.equal(el.childEvents[1], 'composed');
|
||||
assert.equal(el.childEvents[0].type, 'scoped');
|
||||
assert.equal(el.childEvents[1].type, 'composed');
|
||||
});
|
||||
|
||||
test('events set a flag on stopPropagation', function() {
|
||||
var el = fixture('scoped');
|
||||
var ev = el.fireScoped();
|
||||
assert.equal(ev.__propagationStopped, true);
|
||||
});
|
||||
|
||||
test('composed focus and blur events retarget up tree', function() {
|
||||
var el = fixture('focus');
|
||||
el.fireComposed();
|
||||
assert.equal(el.events.length, 2);
|
||||
assert.equal(el.events[0], el.$.child);
|
||||
assert.equal(el.events[1], el);
|
||||
});
|
||||
|
||||
test('scoped focus and blur events do not retarget', function() {
|
||||
var el = fixture('focus');
|
||||
el.fireScoped();
|
||||
assert.equal(el.events.length, 1);
|
||||
assert.equal(el.events[0], el.$.child);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user