17

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.

Yairopro
  • 9,084
  • 6
  • 44
  • 51
  • I have custom elements which comprise two input fields: a primary and a secondary one. I thought I could proxy the return value like you did because I wanted to forward all `HTMLInputElement` prototype property lookups to the primary input, save for a select few, without having to write `get prop(){ return this.#primary.prop; } set prop(x){ this.#primary.prop = x; }` 50 times. Solved it without Proxies by doing this in my module: ``export const Custom = (class Custom extends HTMLElement{ #primary; static init(){ /* Add getters on `this.prototype` accessing `this.#primary`. */ } }).init();``. – Sebastian Simon Oct 21 '22 at 01:11

1 Answers1

10

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 )
Supersharp
  • 29,002
  • 9
  • 92
  • 134
  • I need to encapsulate the logic of the proxy, in the superclass. Like: `function P (_class) { return new Proxy (new class extends _class {}, {}) } class A extends P(HTMLElement) {} customElements.define( 'a-element', A )` But it doesn't work too – Артур Лаврищев Apr 11 '17 at 08:16
  • @АртурЛаврищев use `function P (_class) { return new Proxy (class extends _class {}, {}) }` instead (without `new` before `class`) – Supersharp Apr 11 '17 at 09:56
  • 1
    @АртурЛаврищев thanks for the accepted answer, you can upvote if it helped :-) – Supersharp Apr 14 '17 at 10:13
  • 1
    Thank you so much for this answer. It turned on a lightbulb in my head - there are so many code problems Ive been struggling with for months that might just be elegantly solved by these techniques... – diopside Jul 16 '18 at 05:10
  • @diopside Just curious, what code problems are you looking to solve with these Proxy techniques? – trusktr Nov 30 '19 at 04:46
  • @Supersharp I suppose this Proxy-the-constructor approach could be abstracted into a decorator (legacy decorators in TypeScript, or the new ones once those are out). Wdyt? – trusktr Nov 30 '19 at 22:49
  • How would you create a class B `export class B extends A{` having A wrapped with the proxy? – daniel p Nov 12 '22 at 11:59