javascriptdombrowserreflow

Do browsers optimize setAttribute() and similar when a value is already set?


I often run a conditional to make sure a value has changed before updating the DOM, ex:

const startSize = 10

function updateAttribute(newSize){
  if (newSize === startSize) return
  document.body.setAttribute('data-size', newSize)
}

function updateStyle(newSize){
  if (newSize === startSize) return
  document.body.style.setProperty('--my-size', newSize)
}

Is this an over-optimization? Does the browser already run this check and only apply the new value if it's different from the old?


Solution

  • Setting an element's attribute almost always have (observable) side effects. And these side effects must kick in even when they're set to their current value.

    This is highly visible for instance with the <canvas> element, where setting either its width or height will reset its buffer.

    const canvas = document.querySelector("canvas");
    canvas.getContext("2d").fillRect(50, 50, 80, 80);
    setTimeout(() => {
      canvas.setAttribute("width", canvas.getAttribute("width"));
    }, 3000);
    <canvas></canvas>

    But also with media elements where setting their src or alike attributes will load the resource again, possibly triggering a full re-fetch if the resource isn't cached:

    const img = document.querySelector("img");
    setTimeout(() => {
      img.setAttribute("src", img.getAttribute("src"));
    }, 3000);
    <img src=https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif >

    And even with data- attributes, where we could have thought there wouldn't be any side effects, this is still observable and browsers can't really optimize it out:

    const el = document.querySelector("div");
    setTimeout(() => {
      el.setAttribute("data-foo", el.getAttribute("data-foo"));
    }, 3000);
    const observer = new MutationObserver((recs) => recs.forEach((r) => console.log("attribute %s did change", r.attributeName)));
    observer.observe(el, { attributes: true });
    <div data-foo="bar"></div>

    Now, each attribute's side effect will have different cost, so it's up to you to determine if that cost is worth the check or not (e.g, in my <canvas> it's definitely worth it, but in your data- attribute case, it's less obvious, if you don't have an active observer you can almost ignore it.


    Regarding the CSSOM, the browser will most likely optimize it.
    Apart from through your browser's dev-tools a "no-change update" isn't really observable there. So your .style.setProperty() case corresponds to a simple data- attribute change.