Description
A while back we stopped using direct property access to change attributes, and started using setAttribute
everywhere, via the attr
helper. The reason for this was that we want null/undefined attributes (i.e. class={maybe_undefined}
) to be removed from the element, and there's no way to achieve that via property access (el.className = null
doesn't remove the attribute).
While poking around a benchmark the other day, I learned that el.setAttribute('class', foo)
is significantly slower than el.className = foo
.
So now I'm thinking that we need to consider secret option 3: having a separate function for each attribute that's settable via property:
function remove_attribute(node, name) {
node.removeAttribute(name);
}
function set_class(node, value) {
if (value == null) remove_attribute(node, 'class');
else node.className = value;
}
// later...
set_class(div, ctx.foo);
This obviously means the cost of using a single class
attribute increases, since we need the additional helper. Over the course of an app, that cost gets amortized:
// unminified
function set_class(node, value) {
if (value == null) remove_attribute(node, 'class');
else node.className = value;
}
attr(div, 'class', ctx.foo);
set_class(div, ctx.foo);
// minified
function d(a,b){b==null?e(a,'class'):(a.className=b)} // 53 bytes
a(b,'class',c.foo);
d(b,c.foo); // 8 bytes fewer per occurrence
This benchmark shows that, in cases where values are recalculated but haven't changed, caching values in memory (and only updating the property when they change) yields the best performance, followed by checking the existing property.
Of course, in many apps, if values are being recalculated it's because they likely have changed, in which case not bothering with caching or comparing to the existing property value is obviously faster, but probably not by such a huge margin that it's worth penalising the alternative case. Screenshot of results here (no link because ESBench won't let me save for some reason):
Either way, property access beats setAttribute
handily. (Though I haven't tried other pairs other than class
/className
...)
Thoughts?