J. Doe
J. Doe

Reputation: 333

Reflecting the property and attribute in a custom HTMLInput Element in both directions in native JavaScript

I have a custom input box which inherits from HTMLInputElement:

class TB extends HTMLInputElement {

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

  constructor() {
    super();

    this.addEventListener("change", function (event) {
      this.setAttribute('value', this.value);
    });
    }

    connectedCallback() {
      this.setAttribute('value', this.value);
    }

    attributeChangedCallback(name, oldValue, newValue) { 
        this.value = newValue;
      }
  }

I am able to do the following:

  1. Type "test" in the input

    (tb.value && tb.value..attributes["value"])==="test

  2. Changing the attribute value to change the property

tb.attributes["value"].value ="test" -> tb.value ==="test"

But i cannot do the following:

tb.value = "test" -> tb.attributes["value"] === "test";

I think the solution is to override the get value() and set value(value) of the class. But I do not have any success with it.

Upvotes: 3

Views: 992

Answers (1)

Supersharp
Supersharp

Reputation: 31181

You souldn't do that because it will alter the default behaviour of the <input> element, which is a one-way only binding from the value attribute to the value property.

Anyway, you'll need to overload the value setter and getter combined with super, taking care not to create an infinite loop with the 2 updates.

class TB extends HTMLInputElement {
    static get observedAttributes() { return ['value'] }

    constructor() {
        super()
        this.addEventListener( 'input', () => this.setAttribute( 'value', super.value ) )
    }

    attributeChangedCallback(name, oldValue, newValue) { 
        this.value = newValue
    }
    
    get value() { return super.value }

    set value( val ) {
        super.value = val
        if ( val != this.getAttribute( 'value' ) )
            this.setAttribute( 'value', val )
    }
}
customElements.define( 'my-input', TB, { extends: 'input' } )
<input is="my-input" value="test" id=IN>

NB: it's a simple example where the data types are not checked.

Upvotes: 4

Related Questions