Search code examples
javascripthtmlweb-componentshadow-domcustom-events

How do I bind a function to elements inside of a Shadow DOM?


I'm working with Web Components and try to bind a click event to an element inside of the Shadow DOM.

1. component.html included as <link rel="import" ...> inside of index.html

<template id="my-element">
    <section>
        <header>
            <content select="h1"></content>
            <button></button>
        </header>
        <content select="div"></content>
    </section>
</template>

2. later element usage:

<my-element>
    <h1>Headline</h1>
    <div>...</div>
</my-element>

3. Access element and bind a function to it

Now I want to add an addEventListener() to the <button> inside of my <my-element> (which is unfortunately hidden through the #shadow-root). Like:

var elemBtn = document.querySelector('my-element button');
elemBtn.addEventListener('click', function(event) {
    // do stuff
});

But that won't work. How do I achieve that?


Solution

  • You should be able to do this without involving the window object. Here's a full example:

    <!-- Define element template -->
    <template>
      <h1>Hello World</h1>
      <button id="btn">Click me</button>
    </template>
    
    <!-- Create custom element definition -->
    <script>
      var tmpl = document.querySelector('template');
    
      var WidgetProto = Object.create(HTMLElement.prototype);
    
      WidgetProto.createdCallback = function() {
        var root = this.createShadowRoot();
        root.appendChild(document.importNode(tmpl.content, true));
        // Grab a reference to the button in the shadow root
        var btn = root.querySelector('#btn');
        // Handle the button's click event
        btn.addEventListener('click', this.fireBtn.bind(this));
      };
    
      // Dispatch a custom event when the button is clicked
      WidgetProto.fireBtn = function() {
        this.dispatchEvent(new Event('btn-clicked'));
      };
    
      var Widget = document.registerElement('my-widget', {
        prototype: WidgetProto
      });
    </script>
    
    <!-- Use the element -->
    <my-widget></my-widget>
    
    <!-- Listen for its click event -->
    <script>
      var widget = document.querySelector('my-widget');
      widget.addEventListener('btn-clicked', function() {
        alert('the button was clicked');
      });
    </script>
    

    Example on jsbin