liveBook · Manning

Attribute reflection

As explained in Web Components in Action

The synchronization of an element's content attributes with corresponding JavaScript properties.

A custom element can accept configuration in HTML attributes and through JavaScript properties. Attribute reflection keeps those two ways of using its API in sync: setting a property updates the corresponding attribute, and reading the property after an attribute change returns the new value. It is a useful convention, not a rule every element follows. An input’s value attribute, for example, can retain its initial value while its value property changes. section 1.3 section 3.5

Reflect a slider value

Use the attribute as the source of truth: set value(val) { this.setAttribute('value', val); } get value() { return this.getAttribute('value'); } The same pattern handles backgroundcolor. Declare both names in observedAttributes; attributeChangedCallback can then call refreshSlider or setColor when either changes. A property assignment updates the tag’s attribute, while an attribute set in markup is available through the getter. Those update methods check that the elements they change exist, because the callback can run before they do. section 3.6

Keep the change path one-way to avoid re-entrancy. If attributeChangedCallback assigns a property whose setter sets that same attribute, the two methods can repeatedly trigger each other. Instead, let the setter write the attribute and let the callback perform the resulting work, such as refreshing the UI. section 3.5 section 3.6

Listing 3.19. A pitfall for reflection from Google’s Web Components best practices guide
// When the [checked] attribute changes, set the checked property to match.
attributeChangedCallback(name, oldValue, newValue) {
  if (name === 'checked')
    this.checked = newValue;                                            #1
}

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    // OOPS! This will cause an infinite loop because it triggers the
    // attributeChangedCallback() which then sets this property again.
    this.setAttribute('checked', '');                                   #2
  else
    this.removeAttribute('checked');
}

The checked-property example shows the loop to avoid: an attribute change invokes the setter, which writes the attribute again. section 3.5

Reflection also works for a larger component API. A color picker reflects hex and alpha so an outside attribute change can update its controls. When either value is missing, connectedCallback sets a default through the property; the observed attribute change then drives the same update path. Initialization belongs there rather than in the constructor, which runs too early for attributes to be available. section 11.3

How Web Components in Action covers it

How deep it goes
Works through getters, setters, observed attributes, change callbacks, initialization timing, and a re-entrancy failure.
Examples
Uses a slider’s value and background color, a checked-property infinite loop, and a color picker’s hex and alpha settings.
What it assumes
Assumes familiarity with HTML attributes and JavaScript properties; introduces the Custom Element callbacks needed to connect them.
What you can do after
Build it from scratch with attribute-backed properties and callbacks that update a custom element without re-entrant loops.
How it connects
Connects Custom Elements, HTML attributes, JavaScript properties, attributeChangedCallback, and connectedCallback.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Web Components in Action. The section links point to the parts of the book it draws on.