Skip to content

Commit 07261e4

Browse files
author
Steven Orvell
committed
Add Polymer.dom().notifyObservers method to 'kick' observers, for example, when attributes change under Shadow DOM.
1 parent b11f86b commit 07261e4

6 files changed

Lines changed: 203 additions & 70 deletions

File tree

‎src/lib/dom-api-mutation-content.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
return h;
3131
},
3232

33-
notifyIfNeeded: function() {
33+
notify: function() {
3434
if (this._hasListeners()) {
3535
this._scheduleNotify();
3636
}
@@ -82,7 +82,7 @@
8282
var host = root && root.host;
8383
if (host) {
8484
this._observer = Polymer.dom(host).observeNodes(
85-
this.notifyIfNeeded.bind(this));
85+
this.notify.bind(this));
8686
}
8787
},
8888

‎src/lib/dom-api-mutation.html‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
var DomApi = Polymer.DomApi.ctor;
1616
var Settings = Polymer.Settings;
17+
var hasDomApi = Polymer.DomApi.hasDomApi;
1718

1819
DomApi.Mutation = function(domApi) {
1920
this.domApi = domApi;
@@ -100,7 +101,7 @@
100101

101102
_observeContentElements: function(elements) {
102103
for (var i=0, h, n; (i < elements.length) && (n=elements[i]); i++) {
103-
if (n.localName === 'content') {
104+
if (this._isContent(n)) {
104105
n.__observeNodesMap = n.__observeNodesMap || new WeakMap();
105106
if (n.__observeNodesMap.get(this) === undefined) {
106107
h = Polymer.dom(n).observeNodes(
@@ -113,7 +114,7 @@
113114

114115
_unobserveContentElements: function(elements) {
115116
for (var i=0, n, h; (i < elements.length) && (n=elements[i]); i++) {
116-
if (n.localName === 'content') {
117+
if (this._isContent(n)) {
117118
h = n.__observeNodesMap.get(this);
118119
if (h) {
119120
Polymer.dom(n).unobserveNodes(h);
@@ -122,6 +123,10 @@
122123
}
123124
},
124125

126+
_isContent: function(node) {
127+
return (node.localName === 'content');
128+
},
129+
125130
_callListeners: function(info) {
126131
var o$ = this._listeners;
127132
for (var i=0, o; (i < o$.length) && (o=o$[i]); i++) {
@@ -151,10 +156,13 @@
151156
this._debouncer.context = this;
152157
Polymer.dom.addDebouncer(this._debouncer);
153158
this._preflush = this._flush.bind(this);
154-
Polymer.dom.addPreflush(this._preflush);
159+
160+
}
161+
if (!this._hasListeners()) {
162+
Polymer.dom.addPreflush(this._preflush);
163+
// note: doing this > 1x is a no-op
164+
this._observer.observe(this.node, {childList: true});
155165
}
156-
// note: doing this > 1x is a no-op
157-
this._observer.observe(this.node, {childList: true});
158166
},
159167

160168
_cleanupObserver: function() {

‎src/lib/dom-api.html‎

Lines changed: 16 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,10 @@
538538
if (this.observer) {
539539
this.observer.removeListener(handle);
540540
}
541-
}
541+
},
542+
543+
// abstract, intended as public 'kick' mehanism
544+
notifyObservers: function() {}
542545

543546
};
544547

@@ -744,58 +747,25 @@
744747

745748
DomApi.prototype._distributeParent = function() {};
746749

747-
DomApi.prototype.observeChildren = function(callback) {
748-
if (!this._mo) {
749-
this._mo = new MutationObserver(this._notifyObservers.bind(this));
750-
this._mo.observe(this.node, {childList: true});
751-
// make sure to notify initial state...
752-
this._mutationDebouncer = Polymer.Debounce(this._mutationDebouncer,
753-
function() {
754-
this._notifyObservers([{
755-
target: this.node,
756-
addedNodes: this.childNodes.slice()
757-
}]);
758-
}
759-
);
760-
this._mutationDebouncer.context = this;
761-
Polymer.dom.addDebouncer(this._mutationDebouncer);
762-
}
763-
return this._observers.push(callback);
764-
};
765-
766-
DomApi.prototype._notifyObservers = function(mxns) {
767-
var info = {
768-
target: this.node,
769-
addedNodes: [],
770-
removedNodes: []
771-
};
772-
mxns.forEach(function(m) {
773-
if (m.addedNodes) {
774-
for (var i=0; i < m.addedNodes.length; i++) {
775-
info.addedNodes.push(m.addedNodes[i]);
750+
DomApi.prototype.notifyObservers = function() {
751+
if (this.node.shadowRoot) {
752+
var ip$ = this.node.shadowRoot.querySelectorAll('content');
753+
for (var i=0, c; (i<ip$.length) && (c=ip$[i]); i++) {
754+
this._notifyContent(c);
755+
if (c.parentNode.shadowRoot) {
756+
factory(c.parentNode).notifyObservers();
776757
}
777758
}
778-
if (m.removedNodes) {
779-
for (var i=0; i < m.removedNodes.length; i++) {
780-
info.removedNodes.push(m.removedNodes[i]);
781-
}
782-
}
783-
});
784-
if (info.addedNodes.length || info.removedNodes.length) {
785-
var o$ = this._observers;
786-
for (var i=0, o; (i < o$.length) && (o=o$[i]); i++) {
787-
o.call(null, info);
788-
}
789759
}
790-
};
760+
}
791761

792-
DomApi.prototype.flush = function() {
793-
if (this._mo) {
794-
this._notifyObservers(this._mo.takeRecords());
762+
DomApi.prototype._notifyContent = function(content) {
763+
if (hasDomApi(content) && factory(content).observer) {
764+
factory(content).observer.notify();
795765
}
796-
Polymer.dom.flush();
797766
}
798767

768+
799769
var nativeForwards = ['appendChild', 'insertBefore',
800770
'removeChild', 'replaceChild'];
801771

‎src/mini/shady.html‎

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,6 @@
154154
// NOTE: send a signal to insertion points that we have distributed
155155
// which informs effective children observers
156156
notifyContentObservers(this.shadyRoot);
157-
for (var i=0, c; i < this.shadyRoot._insertionPoints.length; i++) {
158-
c = this.shadyRoot._insertionPoints[i];
159-
if (hasDomApi(c)) {
160-
var dc = Polymer.dom(c);
161-
if (dc.observer) {
162-
dc.observer.notifyIfNeeded();
163-
}
164-
}
165-
}
166157
} else {
167158
if (!this.shadyRoot._hasDistributed) {
168159
this.textContent = '';
@@ -179,11 +170,10 @@
179170
}
180171
// NOTE: send a signal to any Polymer.dom node observers
181172
// to report the initial set of childNodes
182-
var hasDistributed = this.shadyRoot._hasDistributed;
183-
this.shadyRoot._hasDistributed = true;
184-
if (!hasDistributed && hasDomApi(this) && Polymer.dom(this).observer) {
185-
Polymer.dom(this).observer.addAllNodes();
173+
if (!this.shadyRoot._hasDistributed) {
174+
notifyInitialDistribution(this);
186175
}
176+
this.shadyRoot._hasDistributed = true;
187177
}
188178
},
189179

@@ -478,6 +468,24 @@
478468
}
479469
}
480470

471+
function notifyContentObservers(root) {
472+
for (var i=0, c; i < root._insertionPoints.length; i++) {
473+
c = root._insertionPoints[i];
474+
if (hasDomApi(c)) {
475+
var dc = Polymer.dom(c);
476+
if (dc.observer) {
477+
dc.observer.notify();
478+
}
479+
}
480+
}
481+
}
482+
483+
function notifyInitialDistribution(host) {
484+
if (hasDomApi(host) && Polymer.dom(host).observer) {
485+
Polymer.dom(host).observer.addAllNodes();
486+
}
487+
}
488+
481489
var needsUpgrade = window.CustomElements && !CustomElements.useNative;
482490

483491
function upgradeLightChildren(children) {

‎test/smoke/observeReNodes.html‎

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@
2121
Polymer({
2222
is:'test-inner',
2323
ready: function() {
24-
Polymer.dom(this).observeNodes(function(info) {
24+
this.observe();
25+
},
26+
27+
observe: function() {
28+
console.warn('observeNodes', this.localName);
29+
this._childObserver = Polymer.dom(this).observeNodes(function(info) {
2530
info.addedNodes.forEach(function(n) {
2631
if (n.nodeType === Node.ELEMENT_NODE) {
2732
console.log('added:', n.localName, n.textContent);
@@ -33,6 +38,11 @@
3338
}
3439
});
3540
});
41+
},
42+
43+
unobserve: function() {
44+
console.warn('unobserveNodes', this.localName);
45+
Polymer.dom(this).unobserveNodes(this._childObserver);
3646
}
3747
});
3848
</script>
@@ -68,8 +78,8 @@
6878

6979

7080
Polymer.dom.flush();
71-
72-
// setTimeout(function() {
81+
82+
function test() {
7383
console.group('test dynamic');
7484
var d = makeNode('dynamic!');
7585
Polymer.dom.flush();
@@ -91,7 +101,18 @@
91101
Polymer.dom(content).removeChild(Polymer.dom(content).lastChild);
92102
Polymer.dom.flush();
93103
console.groupEnd('test dynamic');
94-
// }, 1000);
104+
}
105+
test();
106+
content.$.inner.unobserve();
107+
test();
108+
content.$.inner.observe();
109+
test();
110+
content.$.inner.unobserve();
111+
test();
112+
content.$.inner.observe();
113+
test();
114+
115+
95116
</script>
96117

97118
</body>

‎test/smoke/observeReNodes2.html‎

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
5+
<title>observeReNodes</title>
6+
7+
<meta charset="utf-8">
8+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
9+
10+
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
11+
<link rel="import" href="../../polymer.html">
12+
13+
</head>
14+
<body>
15+
16+
<dom-module id='test-inner'>
17+
<template>
18+
~<content id="c" select=".a"></content>~
19+
</template>
20+
<script>
21+
Polymer({
22+
is:'test-inner',
23+
ready: function() {
24+
this.observe();
25+
},
26+
27+
observe: function() {
28+
this._childObserver = Polymer.dom(this).observeNodes(function(info) {
29+
info.addedNodes.forEach(function(n) {
30+
if (n.nodeType === Node.ELEMENT_NODE) {
31+
console.log('added:', n.localName, n.textContent);
32+
}
33+
});
34+
info.removedNodes.forEach(function(n) {
35+
if (n.nodeType === Node.ELEMENT_NODE) {
36+
console.log('removed:', n.localName, n.textContent);
37+
}
38+
});
39+
});
40+
this._contentObserver = Polymer.dom(this.$.c).observeNodes(function(info) {
41+
info.addedNodes.forEach(function(n) {
42+
if (n.nodeType === Node.ELEMENT_NODE) {
43+
console.log('%c content added:', 'color: blue;', n.localName, n.textContent);
44+
}
45+
});
46+
info.removedNodes.forEach(function(n) {
47+
if (n.nodeType === Node.ELEMENT_NODE) {
48+
console.log('%c content removed:', 'color: blue;', n.localName, n.textContent);
49+
}
50+
});
51+
}, true);
52+
},
53+
54+
unobserve: function() {
55+
Polymer.dom(this).unobserveNodes(this._childObserver);
56+
Polymer.dom(this).unobserveNodes(this._contentObserver);
57+
}
58+
});
59+
</script>
60+
</dom-module>
61+
62+
<dom-module id='test-content'>
63+
<template>
64+
<test-inner id="inner">[<content id="ip" select=".b"></content>]</test-inner>
65+
</template>
66+
<script>
67+
(function() {
68+
Polymer({
69+
is:'test-content'
70+
});
71+
})();
72+
</script>
73+
</dom-module>
74+
75+
<test-content id="content">
76+
<div class="a b">content A</div>
77+
<div class="a b">content B</div>
78+
</test-content>
79+
80+
<br><br>
81+
82+
<script>
83+
84+
function makeNode(text) {
85+
var d = document.createElement('div');
86+
d.textContent = text;;
87+
return d;
88+
}
89+
90+
91+
Polymer.dom.flush();
92+
93+
// setTimeout(function() {
94+
console.group('test dynamic');
95+
var d = makeNode('dynamic!');
96+
Polymer.dom.flush();
97+
Polymer.dom(content).appendChild(d);
98+
Polymer.dom.flush();
99+
Polymer.dom(content).removeChild(d);
100+
Polymer.dom.flush();
101+
d = makeNode('1');
102+
Polymer.dom(d).classList.add('b');
103+
Polymer.dom(content).appendChild(d);
104+
Polymer.dom.flush();
105+
Polymer.dom(d).classList.add('a');
106+
Polymer.dom(Polymer.dom(d).parentNode).notifyObservers();
107+
Polymer.dom(content.$.inner).appendChild(makeNode('2'));
108+
Polymer.dom.flush();
109+
d = makeNode('-1');
110+
Polymer.dom(d).classList.add('b');
111+
Polymer.dom(d).classList.add('a');
112+
Polymer.dom(content).insertBefore(d, Polymer.dom(content).firstChild);
113+
Polymer.dom.flush();
114+
Polymer.dom(content.$.inner).insertBefore(makeNode('-2'), Polymer.dom(content.$.inner).firstChild);
115+
Polymer.dom.flush();
116+
Polymer.dom(content.$.inner).removeChild(Polymer.dom(content.$.inner).firstChild);
117+
Polymer.dom(content.$.inner).removeChild(Polymer.dom(content.$.inner).lastChild);
118+
Polymer.dom(content).removeChild(Polymer.dom(content).firstChild);
119+
Polymer.dom(content).removeChild(Polymer.dom(content).lastChild);
120+
Polymer.dom.flush();
121+
console.groupEnd('test dynamic');
122+
// }, 1000);
123+
</script>
124+
125+
</body>
126+
</html>

0 commit comments

Comments
 (0)