Skip to content

Commit afd144f

Browse files
monorkinbotandrose
authored andcommitted
Skip beforeAttributeUpdated when an attribute value is unchanged
morphAttributes called the beforeAttributeUpdated callback for every attribute on every element being morphed, before checking whether the incoming value differed from the existing one. The callback's purpose is to let consumers veto a write - there is nothing to veto when the value is already equal, and on dense pages the callback is a hot path.
1 parent 10224e0 commit afd144f

2 files changed

Lines changed: 32 additions & 3 deletions

File tree

‎src/idiomorph.js‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -676,12 +676,13 @@ var Idiomorph = (function () {
676676
const oldAttributes = oldElt.attributes;
677677
const newAttributes = newElt.attributes;
678678
for (const newAttribute of newAttributes) {
679-
if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) {
679+
if (oldElt.getAttribute(newAttribute.name) === newAttribute.value) {
680680
continue;
681681
}
682-
if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) {
683-
oldElt.setAttribute(newAttribute.name, newAttribute.value);
682+
if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) {
683+
continue;
684684
}
685+
oldElt.setAttribute(newAttribute.name, newAttribute.value);
685686
}
686687
// iterate backwards to avoid skipping over items when a delete occurs
687688
for (let i = oldAttributes.length - 1; 0 <= i; i--) {

‎test/hooks.js‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,34 @@ describe("lifecycle hooks", function () {
235235
initial.outerHTML.should.equal(`<a href="#"></a>`);
236236
});
237237

238+
it("does not call beforeAttributeUpdated when the attribute value is unchanged", function () {
239+
let calls = [];
240+
let initial = make(`<a href="#" id="x" class="y"></a>`);
241+
Idiomorph.morph(initial, `<a href="#" id="x" class="y"></a>`, {
242+
callbacks: {
243+
beforeAttributeUpdated: (attributeName, node, mutationType) => {
244+
calls.push([attributeName, mutationType]);
245+
},
246+
},
247+
});
248+
initial.outerHTML.should.equal(`<a href="#" id="x" class="y"></a>`);
249+
calls.should.eql([]);
250+
});
251+
252+
it("calls beforeAttributeUpdated only for attributes that actually change", function () {
253+
let calls = [];
254+
let initial = make(`<a href="a" id="x" class="y"></a>`);
255+
Idiomorph.morph(initial, `<a href="b" id="x" class="y"></a>`, {
256+
callbacks: {
257+
beforeAttributeUpdated: (attributeName, node, mutationType) => {
258+
calls.push([attributeName, mutationType]);
259+
},
260+
},
261+
});
262+
initial.outerHTML.should.equal(`<a href="b" id="x" class="y"></a>`);
263+
calls.should.eql([["href", "update"]]);
264+
});
265+
238266
it("hooks work as expected", function () {
239267
let beginSrc = `
240268
<div>

0 commit comments

Comments
 (0)