Search code examples
javascriptweb-componentcustom-element

Does attributeChangedCallback fire when a property is set programmatically?


If we define a property like this on a color-span custom element:

  static get observedAttributes() {
    return ['bgc'];
  }

And get a reference to an instance like this while also changing the bgc property, should that cause the attributeChangedCallback to fire?

const colorSpan = document.querySelector('color-span');
console.log(colorSpan);
colorSpan.bgc = 'BLUE';

I tried it in this demo, and it does not fire, so I just wanted to confirm my understanding that the attributeChangedCallback will only fire when an attribute is set declaratively like this:

<color-span bgc="RED">Hello From Color Span</color-span>


Solution

  • colorSpan.bgc = 'BLUE'; will set the property, not the attribute. You can set the attribute programmatically using:

    colorSpan.setAttribute('bgc', 'BLUE');
    

    This will trigger the callback. However if you want to sync the property and the attribute you will have to implement that yourself.

    class ColorSpanComponent extends HTMLElement {
    
      static observedAttributes = ['bgc'];
      
      constructor() {
        super();
        this._container = document.createElement('span');
        this._container.append(document.createElement('slot'));
        const shadowRoot = this.attachShadow({mode: 'closed'});
        shadowRoot.append(this._container);
      }
      
      attributeChangedCallback(name, oldValue, newValue) {
        switch (name) {
          case 'bgc': 
            this._container.style.backgroundColor = newValue;  
        }
      }
      
      get bgc() {
        return this._container.style.backgroundColor;
      }
      
      set bgc(value) {
        if (this._container.style.backgroundColor !== value) {
          this._container.style.backgroundColor = value;
          this.setAttribute('bgc', value);
        }
      }
    }
    
    customElements.define('color-span', ColorSpanComponent);
    
    const colorSpan = document.querySelector('color-span');
    // log initial state
    console.log(colorSpan.bgc, colorSpan.getAttribute('bgc'));
    // change attribute and log
    colorSpan.setAttribute('bgc', 'red');
    console.log(colorSpan.bgc, colorSpan.getAttribute('bgc'));
    // change property and log
    colorSpan.bgc = 'blue';
    console.log(colorSpan.bgc, colorSpan.getAttribute('bgc'));
    <color-span bgc="green">TEST</color-span>