エレメントはイベントを利用することで状態の変化をDOMツリーを介して親エレメントに伝達します。Polymer Elementsは、標準のDOM APIを用いてイベントの生成、ディスパッチ、リッスンを行うことができます。
Polymerにはアノテーション付イベントリスナーも用意されています。このリスナーを使用すれば、エレメントのDOMテンプレートの一部としてイベントリスナーを宣言的に記述できます。
アノテーション付イベントリスナーを追加
ローカルDOMの子にイベントリスナーを追加するには、テンプレート内でon-event
アノテーションを使用します。これにより、イベントリスナーをバインドするためだけにエレメントにid
を付与するといった処理が不要になります。
例:
<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>
イベント名はHTML属性を通じて記述されるので、常に小文字に変換されます。これは、HTML属性名が大文字と小文字を区別しないためです。したがって、on-myEvent
を指定すると、イベントmyEvent
に対してリスナーが追加されます。イベントハンドラの名前(例えば、handleClick
)は大文字と小文字を区別します。混乱を避けるため、常にイベント名には小文字を使用するようにして下さい。
リスナーを命令的に追加/削除
標準のaddEventListener
及びremoveEventListener
メソッドを使用して、イベントリスナーを命令的に追加または削除することができます。
Custom Elementにおけるリスナー
Custom Elementにおけるリスナーは、ready()
においてthis.addEventListener()
を使って設定します。リスナーは、Custom ElementがDOMに初めてアタッチされるときに設定されます。
ready() {
super.ready();
this.addEventListener('click', this._onClick);
}
_onClick(event) {
this._makeCoffee();
}
_makeCoffee () {}
イベントハンドラ内のthis
に関して、デフォルトでは、イベントハンドラはthis
値を現在のイベントターゲットに設定して呼び出されます。現在のターゲットは、常にイベントリスナーがアタッチされているエレメント(この場合はCustom Element自体)と同一になります。
子エレメントにおけるリスナー
Custom Elementの子エレメントにリスナーを設定する際は、テンプレート内でアノテーション付きのイベントリスナーを用いる方法が推奨されます。
子エレメントに命令的にリスナーを設定する必要がある場合に重要なことは、 .bind()
を使用してthis
値をバインドするか、アロー関数を使用することです。
ready() {
super.ready();
const childElement = ...
childElement.addEventListener('click', this._onClick.bind(this));
childElement.addEventListener('hover', event => this._onHover(event));
}
エレメント外部におけるリスナー
Custom Elementの外部やその子孫以外(例えば、window
)のイベントをリッスンする場合、イベントリスナーを追加したり削除したりするには、connectedCallback()
と disconnectedCallback()
を適切に利用する必要があります。:
constructor() {
super();
this._boundListener = this._myLocationListener.bind(this);
}
connectedCallback() {
super.connectedCallback();
window.addEventListener('hashchange', this._boundListener);
}
disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener('hashchange', this._boundListener);
}
メモリリークの危険 メモリリークを防止するためdisconnectedCallback
コールバック内でイベントリスナーを削除するようにしてください。自身またはShadow DOMの子のいずれかにイベントリスナーを設定したエレメントは、そのエレメントがガベージコレクトされるのを禁止すべきではありません。しかしながら、ウィンドウまたはドキュメントレベルのような外部のエレメントに設定されたイベントリスナーによって、エレメントがガベージコレクションされないことがあります。
カスタムイベントの発火
ホストエレメントからカスタムイベントを発火するには、標準のCustomEvent
コンストラクタとdispatchEvent
メソッドを使用します。
例:
<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>
CustomEvent
コンストラクタはIEではサポートされていませんが、webcomponents polyfillsには
これをサポートするための小さなポリフィルが含まれており、どこでも同じ構文を使って記述することができます。
デフォルトでは、カスタムイベントはShadow DOMの境界で停止します。カスタムイベントがShadow DOMの境界を越えて伝播するようにするには、イベントを作成する際にcomposed
フラグをtrueに設定します。:
var event = new CustomEvent('my-event', {bubbles: true, composed: true});
後方互換性 レガシーAPIのインスタンスメソッドfire
では、デフォルトでbubbles
とcompos
の両方をtrueが設定されました。最新のAPIで同じように動作させるためには、カスタムイベントを作成する際、上記のように両オプションを指定する必要があります。
リターゲティングされたイベントの処理
Shadow DOMには、イベントがバブルアップする際に、ターゲットを変更する「イベントリターゲッティング(event retargetting)」という機能があり、そのターゲットは常にイベントを受け取るエレメントと同じスコープになります。(例えば、メインドキュメントのリスナーの場合、ターゲットはShadow Tree内ではなくメインドキュメント内のエレメントになります。)
イベントのcomposedPath()
メソッドは、イベントが通過するノード群を配列で返します。そのためevent.composedPath()[0]
は、イベントの原初のターゲットを表します(ただし、ターゲットが閉じられたShadow Root内に隠れていない場合に限ります)。
例:
<!-- 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>
この例では、原初のイベントが<event-retargeting>
エレメントのローカルDOMツリー内の<button>
でトリガーされています。リスナーは、メインドキュメント上の<event-retargeting>
エレメントに対して設定されています。イベントはリターゲティングされるので、エレメントの実装を隠してしまえばクリックイベントは<button>
エレメントからというよりむしろ<event-retargeting>
エレメントから生じているようにみえます。
Shadow Rootはdocument-fragment
としてコンソールに表示されるかもしれません。Shady DOMでは、DocumentFragment
のインスタンスになります。ネイティブのShadow DOMでは、ShadowRoot
(DocumentFragment
を拡張するDOMインターフェース)のインスタンスとして表示されます。
詳細は、Shadow DOMのコンセプトのイベントのリターゲティングを参照して下さい.
プロパティ変更イベント
特定のプロパティ値が変更された際に、ノンバブリングなDOMイベントを発生するエレメントを構築することもできます。詳細については、変更通知イベントを参照してください。