Search code examples
javascripthtmlnative-web-component

custom web component callbacks


I'm having a hard time explaining this. I'm building out a custom component, in this component there is a button. I need to be able to dynamically set the click action for that button so that its action can change depending on context. so if there are 2 instances of this component, a&b, each instance has a different function that is executed when the button inside them is clicked. I thought I could just assign a function to a property of the component and execute the property from inside the component like this:

window.customElements.define('xmp-1', class xmp1 extends HTMLElement{
  constructor(){super();}
  connectedCallback(){
    this.innerHTML = `
      <div>
        <div id='test' style='width:70px; border:solid thin black;'>test button</div>
      </div>`;
    this.querySelector('#test').addEventListener('click', function(){
      console.log('test action');
      this.action1();
    });
  }
});

document.querySelector('xmp-1').action1 = function(){
  console.log("this is the callback");
};
<xmp-1></xmp-1>

but when you run this you get an error saying the property action1 doesn't exist.

Whats the proper way to setup a callback that allows the flexibility I need?


Solution

  • Update: Please try the below code, where I have added a binding statement to bind "this"

    <!DOCTYPE html>
    <html>
    <head>
        <title>
        </title>
    </head>
    <body>
        <xmp-1></xmp-1>
        <script>
            document.querySelector('xmp-1').action1 = function(){
                console.log("this is the callback");
            };
            window.customElements.define('xmp-1', class xmp1 extends HTMLElement {
                constructor() { super(); 
                    this.handler = this.handler.bind(this);
                }
                handler() {
                    console.log("this is the test");
                    console.log('test action');
                    this.action1();
                }
    
                connectedCallback() {
                    this.innerHTML = `<div>
                        <div id='test' style='width:70px; border:solid thin black;'>test button</div>
                        </div>`;
                    this.querySelector('#test').addEventListener('click',this.handler);
                }
            });
        </script>
    </body>
    </html>
    

    Please change xmp-1 to #test in your code as shown below,

    <!DOCTYPE html>
    <html>
    <head>
        <title>
        </title>
    </head>
    <body>
        <xmp-1></xmp-1>
        <script>
            window.addEventListener('load', (event) => {
    
                document.querySelector('#test').action1 = function(){
                    console.log("this is the callback");
                };
    
            });
            window.customElements.define('xmp-1', class xmp1 extends HTMLElement {
                constructor() { super(); }
    
                connectedCallback() {
                    this.innerHTML = `<div>
                        <div id='test' style='width:70px; border:solid thin black;'>test button</div>
                        </div>`;
                    this.querySelector('#test').addEventListener('click', function () {
                        console.log('test action');
                        this.action1();
                    });
                }
            });
        </script>
    </body>
    </html>
    

    Or please try approach2 as shown below, where you must add a function called this.querySelector('#test').action1 to your custom element.

    <!DOCTYPE html>
    <html>
    <head>
        <title>
        </title>
    </head>
    <body>
        <xmp-1></xmp-1>
        <script>
            window.customElements.define('xmp-1', class xmp1 extends HTMLElement {
                constructor() { super(); }
    
                connectedCallback() {
                    this.innerHTML = `<div>
                        <div id='test' style='width:70px; border:solid thin black;'>test button</div>
                        </div>`;
                    this.querySelector('#test').addEventListener('click', function () {
                        console.log('test action');
                        this.action1();
                    });
                    this.querySelector('#test').action1 = function() {
                        console.log('test action1');
                    }
                }
            });
        </script>
    </body>
    </html>