git ssb

1+

Matt McKegg / mutant



Commit 955ee4ba0914a6c2c9a1158bdfa7ea7a186ca900

html-element: handle teardown of observables when elements removed from dom

Matt McKegg committed on 7/22/2016, 12:27:11 PM
Parent: 1b3ee2905a0cb00a98265dfd85dc66f2e87d675b

Files changed

html-element.jschanged
lib/apply-properties.jschanged
html-element.jsView
@@ -11,14 +11,10 @@
1111 return Element.bind(this, document, namespace)
1212 }
1313
1414 module.exports.destroy = function (node) {
15- var data = caches.get(node)
16- if (data) {
17- Array.from(data.releases.values()).forEach(tryInvoke)
18- caches.delete(node)
19- }
20- applyProperties.destroy(node)
15 + unbind(node)
16 + caches.delete(node)
2117 }
2218
2319 function Element (document, namespace, tagName, properties, children) {
2420 if (!children && (Array.isArray(properties) || isText(properties))) {
@@ -42,17 +38,23 @@
4238 }
4339
4440 var data = {
4541 targets: new Map(),
46- releases: new Map()
42 + bindings: []
4743 }
4844
4945 caches.set(node, data)
50- applyProperties(node, properties)
46 + var hooks = applyProperties(node, properties, data)
5147 if (children != null) {
5248 appendChild(document, node, data, children)
5349 }
5450
51 + if (Array.isArray(hooks)) {
52 + hooks.forEach(function (v) {
53 + data.bindings.push(new HookBinding(v, node))
54 + })
55 + }
56 +
5557 return node
5658 }
5759
5860 function appendChild (document, target, data, node) {
@@ -63,9 +65,9 @@
6365 } else if (isObservable(node)) {
6466 var nodes = getNodes(document, resolve(node))
6567 nodes.forEach(append, target)
6668 data.targets.set(node, nodes)
67- data.releases.set(node, bind(document, node, data))
69 + data.bindings.push(new Binding(bind, document, node, data))
6870 } else {
6971 target.appendChild(getNode(document, node))
7072 }
7173 }
@@ -92,16 +94,18 @@
9294 oldNodes.filter(function (node) {
9395 return !~newNodes.indexOf(node)
9496 }).forEach(function (node) {
9597 parent.removeChild(node)
98 + unbind(node)
9699 })
97100 if (marker) {
98101 newNodes.forEach(function (node) {
99102 parent.insertBefore(node, marker)
100103 })
101104 } else {
102105 newNodes.forEach(function (node) {
103106 parent.appendChild(node)
107 + rebind(node)
104108 })
105109 }
106110 }
107111
@@ -138,8 +142,40 @@
138142 return [getNode(document, nodeOrNodes)]
139143 }
140144 }
141145
146 +function rebind (node) {
147 + if (node.nodeType === 1) {
148 + var data = caches.get(node)
149 + if (data) {
150 + data.bindings.forEach(invokeBind)
151 + }
152 + for (var i = 0; i < node.childNodes.length; i++) {
153 + rebind(node.childNodes[i])
154 + }
155 + }
156 +}
157 +
158 +function unbind (node) {
159 + if (node.nodeType === 1) {
160 + var data = caches.get(node)
161 + if (data) {
162 + data.bindings.forEach(invokeUnbind)
163 + }
164 + for (var i = 0; i < node.childNodes.length; i++) {
165 + unbind(node.childNodes[i])
166 + }
167 + }
168 +}
169 +
170 +function invokeBind (binding) {
171 + binding.bind()
172 +}
173 +
174 +function invokeUnbind (binding) {
175 + binding.unbind()
176 +}
177 +
142178 function push (item) {
143179 this.push(item)
144180 }
145181
@@ -151,4 +187,50 @@
151187 if (typeof func === 'function') {
152188 func()
153189 }
154190 }
191 +
192 +function HookBinding (fn, element) {
193 + this.element = element
194 + this.fn = fn
195 + this.bind()
196 +}
197 +
198 +HookBinding.prototype = {
199 + bind: function () {
200 + if (!this.bound) {
201 + this._release = this.fn(this.element)
202 + this.bound = true
203 + }
204 + },
205 + unbind: function () {
206 + if (this.bound && typeof this._release === 'function') {
207 + this._release()
208 + this._release = null
209 + this.bound = false
210 + }
211 + }
212 +}
213 +
214 +function Binding (fn, document, obs, data) {
215 + this.document = document
216 + this.obs = obs
217 + this.data = data
218 + this.fn = fn
219 + this.bind()
220 +}
221 +
222 +Binding.prototype = {
223 + bind: function () {
224 + if (!this.bound) {
225 + this._release = this.fn(this.document, this.obs, this.data)
226 + this.bound = true
227 + }
228 + },
229 + unbind: function () {
230 + if (this.bound && typeof this._release === 'function') {
231 + this._release()
232 + this._release = null
233 + this.bound = false
234 + }
235 + }
236 +}
lib/apply-properties.jsView
@@ -1,23 +1,18 @@
11 var isObservable = require('../is-observable')
22 var Set = require('../set')
33 var watch = require('../watch')
4-var caches = new global.WeakMap()
54
65 module.exports = applyProperties
76
8-function applyProperties (target, properties) {
9- var data = caches.get(target)
10- if (!data) {
11- data = { releases: [] }
12- caches.set(target, data)
13- }
14-
7 +function applyProperties (target, properties, data) {
158 var classList = Set()
169 if (target.className) {
1710 classList.add(target.className)
1811 }
1912
13 + var hooks = null
14 +
2015 for (var key in properties) {
2116 var valueOrObs = properties[key]
2217 var value = resolve(valueOrObs)
2318
@@ -28,26 +23,21 @@
2823 var styleObs = isObservable(value[k]) ? value[k] : null
2924 target.style.setProperty(k, styleValue)
3025
3126 if (styleObs) {
32- data.releases.push(bindStyle(target, styleObs, k))
27 + data.bindings.push(new Binding(bindStyle, target, styleObs, k))
3328 }
3429 }
3530 } else if (key === 'hooks') {
36- value.forEach(function (v) {
37- var release = v(target)
38- if (typeof release === 'function') {
39- data.releases.push(release)
40- }
41- })
31 + hooks = value
4232 } else if (key === 'attributes') {
4333 for (var k in value) {
4434 var attrValue = resolve(value[k])
4535 var attrObs = isObservable(value[k]) ? value[k] : null
4636 target.setAttribute(k, attrValue)
4737
4838 if (attrObs) {
49- data.releases.push(bindAttr(target, attrObs, k))
39 + data.bindings.push(new Binding(bindAttr, target, attrObs, k))
5040 }
5141 }
5242 } else if (key === 'events') {
5343 for (var name in value) {
@@ -66,9 +56,9 @@
6656 } else {
6757 target[key] = value
6858 var obs = isObservable(valueOrObs) ? valueOrObs : null
6959 if (obs) {
70- data.releases.push(bind(target, obs, key))
60 + data.bindings.push(new Binding(bind, target, obs, key))
7161 }
7262 }
7363 }
7464
@@ -77,18 +67,10 @@
7767 if (value || target.className) {
7868 target.className = value
7969 }
8070 })
81-}
8271
83-applyProperties.destroy = function (target) {
84- var data = caches.get(target)
85- if (data) {
86- while (data.releases.length) {
87- data.releases.pop()()
88- }
89- caches.delete(target)
90- }
72 + return hooks
9173 }
9274
9375 function bindStyle (target, styleObs, key) {
9476 return styleObs(function (value) {
@@ -121,4 +103,28 @@
121103
122104 function resolve (source) {
123105 return typeof source === 'function' ? source() : source
124106 }
107 +
108 +function Binding (fn, element, source, key) {
109 + this.element = element
110 + this.source = source
111 + this.key = key
112 + this.fn = fn
113 + this.bind()
114 +}
115 +
116 +Binding.prototype = {
117 + bind: function () {
118 + if (!this.bound) {
119 + this._release = this.fn(this.element, this.source, this.key)
120 + this.bound = true
121 + }
122 + },
123 + unbind: function () {
124 + if (this.bound) {
125 + this._release()
126 + this._release = null
127 + this.bound = false
128 + }
129 + }
130 +}

Built with git-ssb-web