【Polymer 1.0】イベント
目次
イベントリスナの設定
ホストエレメント(自身のエレメント)のイベントリスナを設定するにはlisteners
オブジェクトを使用し、イベントとイベントハンドラを関連付けます。
listeners
には、イベント名と関連付けるイベントハンドラの関数名を指定します:
Polymer({ is: 'x-custom', listeners: { 'tap': '_onTap' }, _onTap: function (e, detail) { // イベント発生時の処理を記述 } });
またthis.$ハッシュに格納されるid
付きのノードも指定できます:
<dom-module id="x-custom"> <template> <span id="playground">Tap me!</span> </template> <script> Polymer({ is: 'x-custom', listeners: { // this.$ハッシュに格納されるid付きノードを指定できる 'playground.tap': '_playgroundOnTap' }, _playgroundOnTap: function (e, detail) { // id="playground"のspanがタップされた際の処理を記述 } }); </script> </dom-module>
アノテーションによるイベントリスナの設定
<template>
内にあるエレメントのイベントリスナの設定方法としてlisteners
のイベントリスナ設定で必要だったエレメントのid
付与を行わなくてすみます。
<dom-module id="x-custom"> <template> <button on-click="_buttonOnClick">Click me!</button> </template> <script> Polymer({ is: 'x-custom', _buttonOnClick: function (e) { alert('_buttonOnClick'); } }); </script> </dom-module>
イベントアノテーションはHTML属性で指定するので、イベント名は常に小文字化されます。これはHTMLが属性名の大文字と小文字を区別しないためです。例えば、on-myEvent
というイベントアノテーションは、myevent
というイベントリスナの設定を行います。ただしイベントハンドラ名(上記コードでは_buttonOnClick
)は大文字と小文字を区別します。
Note: ここで説明したように、イベントアノテーションは小文字に変換されます(
on-myEvent
というイベントアノテーションがmyevent
に変換されるように)。混乱をさけるため、イベント名は常に小文字を使用してください。
カスタムイベント
ホストエレメントからカスタムイベントを発火するにはfire()
メソッドを使用します。またイベントハンドラに固有のデータを渡したい場合は、fire()
メソッドの引数にデータを渡してください。イベントハンドラに渡されたデータは、イベントオブジェクトのdetail
というプロパティに設定されています。
<!DOCTYPE html> <html> <head> <script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> <link rel="import" href="../bower_components/polymer/polymer.html"> </head> <body> <x-custom></x-custom> <dom-module id="x-custom"> <template> <button on-click="_fireKickOnClick">Kick Me</button> </template> <script> document.addEventListener('WebComponentsReady', function () { Polymer({ is: 'x-custom', _fireKickOnClick: function (e, detail) { // "kick"というカスタムイベントに"{kicked: true}" // というデータを付加して発火する。 this.fire('kick', {kicked: true}); } }); }); </script> </dom-module> <script> // "kick"カスタムイベントのハンドラです document.querySelector('x-custom').addEventListener('kick', function (e) { // "kick"カスタムイベントに付加されたデータ"kicked"を表示(trueが表示される) alert('kicked: ' + e.detail.kicked); }) </script> </body> </html>
ジェスチャーイベント
Polymerでは、ユーザーインタラクションのイベントに対してカスタム"ジェスチャー"イベントを自動で発火します。これらのイベントはタッチまたはマウス環境で一貫して発火されるので、mouse〜
やtouch〜
の代わりとなる同等のイベントを使用することが推奨されます。例えば、click
よりtap
を使用することでクロスプラットフォームを実現できます。
以下はサポートされるジェスチャーイベントの簡単な説明と、イベントハンドラが受け取るイベントオブジェクトのevent.detail
で利用可能なプロパティを記載してます:
down
- finger/button のdownイベントx
- clientXイベント座標y
- clientYイベント座標sourceEvent
-down
アクションの起因となるオリジナルDOMイベント
up
- finger/button のupイベントx
- clientXイベント座標y
- clientYイベント座標sourceEvent
-up
アクションの起因となるオリジナルDOMイベント
tap
-down
とup
が起因となるイベントx
- clientXイベント座標y
- clientYイベント座標sourceEvent
-tap
アクションの起因となるオリジナルDOMイベント
track
-down
状態の間の finger/button のmoveイベントstate
- トラッキング状態を示す文字列:start
- トラッキングが最初に見つけられ発火した状態(finger/buttondown
され、一定間隔moveした状態)track
- トラッキングしている状態end
- トラッキングが終了している状態
x
- clientXイベント座標y
- clientYイベント座標dx
- 最初にtrack
イベントがあった場所からの横ピクセルの移動量dy
- 最初にtrack
イベントがあった場所からの縦ピクセルの移動量ddx
- 最後にtrack
イベントがあった場所からの横ピクセルの移動量ddy
- 最後にtrack
イベントがあった場所からの縦ピクセルの移動量hover()
- 現在ホバーされているエレメントを決定するために呼び出される関数
次にジェスチャーイベントの例を示します。灰色の領域でドラッグを行うと、その動作ログが「Drag me!」の領域に表示されます:
<dom-module id="drag-me"> <style> #playground { width: 350px; height: 350px; background: gray; } </style> <template> <div>{{message}}</div> <div id="playground" on-track="_playgroundOnTrack"></div> </template> <script> Polymer({ is: 'drag-me', properties: { message: {type: String, value: 'Drag me!'} }, _playgroundOnTrack: function (e) { switch (e.detail.state) { case 'start': this.message = 'Tracking started!'; break; case 'track': this.message = 'Tracking in progress... ' + e.detail.x + ', ' + e.detail.y; break; case 'end': this.message = 'Tracking ended!'; break; } } }); </script> </dom-module>
ジェスチャーイベントをハンドリングすることでタッチ環境でのスクロール制御を行うことができます。例えば、タッチ環境でtrack
イベントをハンドリングしているエレメントをスワイプしても、デフォルトでは画面がスクロールしないようになっています。スクロール動作はthis.setScrollDirection(direction, node)
メソッドで制御することができます。direction
には「'x'
, 'y'
, 'none'
, 'all'
」のいずれかを指定し、node
には対象のエレメントを指定します。
次のサンプルはタッチ環境の端末(スマートフォンなど)で実行してみてください。スクロール"なし"を選択して灰色の領域をスワイプしても青色の領域はスクロールしません。スクロール"あり"を選択して灰色の領域をスワイプすると青色の領域がスクロールします:
<dom-module id="drag-me"> <template> <style> :host { display: block; width: 10000px; height: 10000px; color: white; background-color: blue;; } .container { position: relative; left: 50px; top: 50px; } #playground { width: 350px; height: 350px; background: gray; } </style> <div class="container"> <!-- メッセージ領域 --> <div>{{message}}</div> <!-- スクロールあり/なしのラジオボタン --> <div> <span>スクロール: </span> <input type="radio" id="directionOff" name="direction" value="none" on-change="_changeDirection" checked>なし <input type="radio" id="directionOn" name="direction" value="all" on-change="_changeDirection">あり <span>(タッチ環境用の設定)</span> </div> <!-- ドラッグ領域 --> <div id="playground" on-track="_handleTrack"></div> </div> </template> <script> Polymer({ is: 'drag-me', properties: { message: {type: String, value: 'Drag me!'} }, ready: function () { this._changeDirection(); }, // ドラッグ領域でトラックが発生した際のハンドラ _handleTrack: function (e) { switch (e.detail.state) { case 'start': this.message = 'Tracking started!'; break; case 'track': this.message = 'Tracking in progress... ' + e.detail.x + ', ' + e.detail.y; break; case 'end': this.message = 'Tracking ended!'; break; } }, // スクロールのあり/なしを変更するメソッド _changeDirection: function () { if (this.$.directionOff.checked) { this.setScrollDirection('none', this.$.playground); } else if (this.$.directionOn.checked) { this.setScrollDirection('all', this.$.playground); } } }); </script> </dom-module>
Eventの再標的化(Event retargeting)
Shadow DOMにはEventの再標的化(Event retargeting)と呼ばれる機能があります。この機能によって、イベント発生元のエレメントが置き換えられ、カスタムエレメント自身でイベントが発生したかのような動作になります。Shady DOMは再標的化が行われないので、環境に依存した振る舞いをすることになります。
Polymer.dom()
メソッドの引数にイベントハンドラのイベントオブジェクトを指定することで、Polymerによって標準化されたイベントオブジェクトを取得できます。このオブジェクトを使用することによって、Shadow DOMとShady DOM両方の環境で同じイベントターゲットを取得することができます。標準化されたイベントオブジェクトは次のプロパティを持ちます:
Polymer.dom(event).rootTarget
: Shadow DOMが再標的化される前の、オリジナルまたはルートのターゲットです。Polymer.dom(event).localTarget
: 再標的化されたターゲット、つまりカスタムエレメント自身です。Polymer.dom(event).path
: イベントがどのノードを通過してきたかを示すパスです。
まずは次のサンプルをShady DOM環境で実行し、「Click me!」をクリックした際のイベントのターゲットを確認してみましょう:
Shady DOMで実行するにはクエリパラメータ「dom=shady」を付加します。
例: http://localhost:3000/src/events/retargeting.html?dom=shady
<dom-module id="x-custom"> <template> <span id="clickMe" style="border: solid blue">Click me!</span> </template> <script> Polymer({is: 'x-custom'}); </script> </dom-module>
<!DOCTYPE html> <html> ... <body> <x-custom></x-custom> <script> document.querySelector('x-custom').addEventListener('click', function (event) { console.log('■■■ rootTarget ■■■'); console.log(Polymer.dom(event).rootTarget); console.log('■■■ localTarget ■■■ '); console.log(Polymer.dom(event).localTarget); console.log('■■■ path ■■■ '); console.log(Polymer.dom(event).path); console.log('■■■ target ■■■'); console.log(event.target); }); </script> </body> </html>
Shady DOM環境で取得したイベントのターゲットは次のようになります:
Polymer.dom(event).rootTarget
: クリックしたエレメント(id="clickMe"のspan)。Polymer.dom(event).localTarget
: カスタムエレメント(x-custum
)自身。event.target
: クリックしたエレメント(id="clickMe"のspan)。
次はShadow DOM環境で実行した際のイベントのターゲットを確認してみましょう。
Shadow DOMで実行するにはクエリパラメータ「dom=shadow」を付加します。
例: http://localhost:3000/src/events/retargeting.html?dom=shadow
Shadow DOM環境で取得したイベントのターゲットは次のようになります:
Polymer.dom(event).rootTarget
: クリックしたエレメント(id="clickMe"のspan)。Polymer.dom(event).localTarget
: カスタムエレメント(x-custum
)自身。event.target
: カスタムエレメント(x-custum
)自身。
結果として、Polymer.dom(event)
で取得したイベントのターゲットは、Shady DOMでもShadow DOMでも同じでした。ただしevent.target
はShady DOMとShadow DOMでは取得されるものが異なるため、カスタムエレメントのイベントのターゲットを取得する場合はPolymer.dom(event)
を使用するようにしてください。