Home Start Get started Quick tour of Polymer Install the Release Candidate Polymer Feature overview What's new About Polymer 2.0 Upgrade guide Hybrid elements Release notes Custom elements Custom element concepts Define an element Declare properties Shadow DOM & styling Shadow DOM concepts DOM templating Style shadow DOM Custom CSS properties Events Handle and fire events Gesture events Data system Data system concepts Work with object and array data Observers and computed properties Data binding Helper elements Tools Tools overview Polymer CLI Document your elements Test your elements Optimize for production Advanced tools Services polymer.json specification Node support Resources Browser compatibility Glossary API Reference API Reference App Toolbox What's in the box? Using the Toolbox App templates Responsive app layout Routing Localization App storage Service worker Serve your app Case study Shop News Blog Community Feature overview
What's new
About Polymer 2.0 Upgrade guide Hybrid elements Release notes
Custom elements
Custom element concepts Define an element Declare properties
Shadow DOM & styling
Shadow DOM concepts DOM templating Style shadow DOM Custom CSS properties
Events
Handle and fire events Gesture events
Data system
Data system concepts Work with object and array data Observers and computed properties Data binding Helper elements
Tools
Tools overview Polymer CLI Document your elements Test your elements Optimize for production Advanced tools Services polymer.json specification Node support
Resources
Browser compatibility Glossary
API Reference
API Reference

Elements use events to communicate state changes up the DOM tree to parent elements. Polymer elements can use the standard DOM APIs for creating, dispatching, and listening for events.

Polymer also provides annotated event listeners, which allow you to specify event listeners declaratively as part of the element's DOM template.

To add event listeners to local DOM children, use on-event annotations in your template. This often eliminates the need to give an element an id solely for the purpose of binding an event listener.

Example:

<dom-module id="x-custom">
  <template>
    <button on-click="handleClick">Kick Me</button>
  </template>
  <script>
    class XCustom extends Polymer.Element {

      static get is() {return 'x-custom'}

      handleClick() {
        console.log('Ow!');
      }
    }
    customElements.define(XCustom.is, XCustom);
  </script>
</dom-module>

Tip: Use on-tap rather than on-click for an event that fires consistently across both touch (mobile) and click (desktop) devices. The tap event is part of an optional set of gesture events. See Gesture events for information on enabling gesture event support for your element.

Because the event name is specified using an HTML attribute, the event name is always converted to lowercase. This is because HTML attribute names are case insensitive. So specifying on-myEvent adds a listener for myevent. The event handler name (for example, handleClick) is case sensitive. To avoid confusion, always use lowercase event names.

You can use the standard addEventListener and removeEventListener methods to add and remove event listeners imperatively.

ready() {
  super.ready();
  this.addEventListener('click', e => this._myClickListener(e));
}

The previous example uses an arrow function to ensure the listener is called with the element as the this value. You can also use bind to create a bound instance of the listener function. This can be useful if you need to remove the listener.

constructor() {
  super();
  this._boundListener = this._myLocationListener.bind(this);
}

connectedCallback() {
  super.connectedCallback();
  window.addEventListener('hashchange', this._boundListener);
}

disconnectedCallback() {
  super.disconnectedCallback();
  window.removeEventListener('hashchange', this._boundListener);
}

An element adding an event listener to itself or one of its shadow DOM children shouldn't prevent the element from being garbage collected. However, an event listener attached to an outside element, like a window or document level event listener, may prevent the element from being garbage collected. Remove the event listener in disconnectedCallback to prevent memory leaks.

To fire a custom event from the host element use the standard CustomEvent constructor and the dispatchEvent method.

Example:

<dom-module id="x-custom">
  <template>
    <button on-click="handleClick">Kick Me</button>
  </template>

  <script>
    class XCustom extends Polymer.Element {

      static get is() {return 'x-custom'}

      handleClick(e) {
        this.dispatchEvent(new CustomEvent('kick', {detail: {kicked: true}}));
      }
    }
    customElements.define(XCustom.is, XCustom);
  </script>

</dom-module>
<x-custom></x-custom>

<script>
    document.querySelector('x-custom').addEventListener('kick', function (e) {
        console.log(e.detail.kicked); // true
    })
</script>

By default, custom events stop at shadow DOM boundaries. To make a custom event pass through shadow DOM boundaries, set the composed flag to true when you create the event:

var event = new CustomEvent('my-event', {bubbles: true, composed: true});

Shadow DOM has a feature called "event retargeting" which changes an event's target as it bubbles up, such that target is always in the same scope as the receiving element. (For example, for a listener in the main document, the target is an element in the main document, not in a shadow tree.)

The event's composedPath() method returns an aray of nodes through which the event will pass. So event.composedPath()[0] represents the original target for the event (unless that target is hidden in a closed shadow root).

Example:

<!-- event-retargeting.html -->
 ...
<dom-module id="event-retargeting">
  <template>
    <button id="myButton">Click Me</button>
  </template>

  <script>
    class EventRetargeting extends Polymer.Element {
      static get is() {return 'event-retargeting'}

      ready() {
        super.ready();
        this.$.myButton.addEventListener('click', e => {this._handleClick(e)});
      }

      _handleClick(e) {
        console.info(e.target.id + ' was clicked.');
      }

    }

    customElements.define(EventRetargeting.is, EventRetargeting);
  </script>
</dom-module>


<!-- index.html  -->
  ...
<event-retargeting></event-retargeting>

<script>
  var el = document.querySelector('event-retargeting');
  el.addEventListener('click', function(e){
    // logs the instance of event-targeting that hosts #myButton
    console.info('target is:', e.target);
    // logs [#myButton, ShadowRoot, event-retargeting,
    //       body, html, document, Window]
    console.info('composedPath is:', e.composedPath());
  });
</script>

In this example, the original event is triggered on a <button> inside the <event-retargeting> element's local DOM tree. The listener is added on the <event-retargeting> element itself, which is in the main document. To hide the implementation of the element, the event should be retargeted so it appears to come from <event-retargeting> rather than from the <button> element.

The shadow root may show up in the console as document-fragment. In shady DOM this is an instance of DocumentFragment. In native shadow DOM, this would show up as an instance of ShadowRoot (a DOM interface that extends DocumentFragment).

You can configure an element to fire a non-bubbling DOM event when a specified property changes. For more information, see Change notification events.