tangled
alpha
login
or
join now
yippee.fun
/
morphlex
0
fork
atom
Precise DOM morphing
morphing
typescript
dom
0
fork
atom
overview
issues
pulls
pipelines
Performance improvements
joel.drapper.me
4 months ago
e7c28889
6c764bfd
+28
-9
1 changed file
expand all
collapse all
unified
split
src
morphlex.ts
+28
-9
src/morphlex.ts
···
129
}
130
131
private morphOneToOne(from: ChildNode, to: ChildNode): void {
0
0
0
132
if (!(this.options.beforeNodeMorphed?.(from, to) ?? true)) return
133
134
const pair: PairOfNodes<ChildNode> = [from, to]
···
167
}
168
169
private morphAttributes([from, to]: PairOfMatchingElements<Element>): void {
170
-
// Remove any excess attributes from the element that aren’t present in the reference.
171
-
for (const { name, value } of from.attributes) {
172
-
if (!to.hasAttribute(name) && (this.options.beforeAttributeUpdated?.(from, name, null) ?? true)) {
173
-
from.removeAttribute(name)
174
-
this.options.afterAttributeUpdated?.(from, name, value)
175
-
}
176
-
}
177
178
-
// Copy attributes from the reference to the element, if they don’t already match.
179
-
for (const { name, value } of to.attributes) {
0
0
0
180
const oldValue = from.getAttribute(name)
0
181
if (oldValue !== value && (this.options.beforeAttributeUpdated?.(from, name, value) ?? true)) {
182
from.setAttribute(name, value)
183
this.options.afterAttributeUpdated?.(from, name, oldValue)
0
0
0
0
0
0
0
0
0
0
0
0
184
}
185
}
186
}
···
264
const toChildNode = toChildNodes[i]!
265
266
if (fromChildNode) {
0
0
0
0
0
267
if (isElement(toChildNode)) {
268
this.searchSiblingsToMorphChildElement(fromChildNode, toChildNode, from)
269
} else {
···
129
}
130
131
private morphOneToOne(from: ChildNode, to: ChildNode): void {
132
+
// Fast path: if nodes are exactly the same object, skip morphing
133
+
if (from.isSameNode?.(to)) return
134
+
135
if (!(this.options.beforeNodeMorphed?.(from, to) ?? true)) return
136
137
const pair: PairOfNodes<ChildNode> = [from, to]
···
170
}
171
172
private morphAttributes([from, to]: PairOfMatchingElements<Element>): void {
173
+
const toAttrs = to.attributes
174
+
const fromAttrs = from.attributes
0
0
0
0
0
175
176
+
// First pass: update/add attributes from reference (iterate forwards)
177
+
for (let i = 0; i < toAttrs.length; i++) {
178
+
const attr = toAttrs[i]!
179
+
const name = attr.name
180
+
const value = attr.value
181
const oldValue = from.getAttribute(name)
182
+
183
if (oldValue !== value && (this.options.beforeAttributeUpdated?.(from, name, value) ?? true)) {
184
from.setAttribute(name, value)
185
this.options.afterAttributeUpdated?.(from, name, oldValue)
186
+
}
187
+
}
188
+
189
+
// Second pass: remove excess attributes (iterate backwards for efficiency)
190
+
for (let i = fromAttrs.length - 1; i >= 0; i--) {
191
+
const attr = fromAttrs[i]!
192
+
const name = attr.name
193
+
const value = attr.value
194
+
195
+
if (!to.hasAttribute(name) && (this.options.beforeAttributeUpdated?.(from, name, null) ?? true)) {
196
+
from.removeAttribute(name)
197
+
this.options.afterAttributeUpdated?.(from, name, value)
198
}
199
}
200
}
···
278
const toChildNode = toChildNodes[i]!
279
280
if (fromChildNode) {
281
+
// Fast path: if nodes are exactly the same, skip morphing
282
+
if (fromChildNode.isSameNode?.(toChildNode)) {
283
+
continue
284
+
}
285
+
286
if (isElement(toChildNode)) {
287
this.searchSiblingsToMorphChildElement(fromChildNode, toChildNode, from)
288
} else {