Search code examples
javascriptweb-componentcustom-elementes6-classes6-proxy

How to Proxy Custom Element (Web Component)


class A extends HTMLElement {
  constructor() {
    super()
    return new Proxy(this, {})
  }
}

window.customElements.define('a-element', A)
<a-element></a-element>

How can i Proxy custom element?

When i try it:

Uncaught InvalidStateError: custom element constructors must call super() first and must not return a different object.


Solution

  • You can either Proxify a class or an instance of a Custom Element.

    Given the following Custom Element definition:

    class A extends HTMLElement {
      constructor() {
        super()
      }
      connectedCallback() {
        this.innerHTML = 'Hello'    
      }
    }
    customElements.define( 'a-element', A )
    

    Proxy for a Custom Element instance

    Create a Proxy from an instance reference (here: ae):

    <a-element id="ae"></a-element>
    <script>
      var b1 = new Proxy( ae, {
        get ( target, value ) { return target[value] }       
      } )
      console.log( b1.innerHTML ) // => "Hello"
    </script>
    

    Proxy for a Custom Element class

    Define the construct trap to catch the new operator:

    <script>
      var B = new Proxy( A, {
        construct() { return new A }
      } )
      var b2 = new B
      document.body.appendChild( b2 )
      console.log( b2.innerHTML ) // => "Hello"
    </script>
    

    Get a Custom Element instance Proxy from a class Proxy

    If you want to instanciate a Custom Element and get one Proxy object directly, you can combine both solutions above.

    The following example shows how to get a Proxy for element <a-element> that will log in the console each property access. The construct() trap defined in the class Proxy returns itself a Proxy for the instanciated Custom Element.

    class A extends HTMLElement {
      constructor() {
        super()
      }
      connectedCallback() {
        this.innerHTML = 'Hello'    
      }
    }
    customElements.define( 'a-element', A )		
    
    var P = new Proxy( A, {
      construct () { 
        return new Proxy ( new A, {
          get ( target, value ) { 
            if ( value == 'element' ) 
              return target
            console.info( `proxy: property ${value} for <${target.localName}> is "${target[value]}"` )
            return target[value]
          }       
        } )
      }
    } )
    var p = new P
    document.body.appendChild( p.element )
    console.log( p.innerHTML )