Skip to content

Revisit setAttribute vs setting properties #3750

Closed
@Rich-Harris

Description

@Rich-Harris

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.

Screen Shot 2019-10-19 at 14 37 11

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):

Screen Shot 2019-10-19 at 14 33 46

Either way, property access beats setAttribute handily. (Though I haven't tried other pairs other than class/className...)

Thoughts?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions