iOSにおけるclickイベントの発生条件まとめ
- HTML5 APIs
- UI ライブラリ
- テスト支援
- ドキュメント
- コード品質ツール
- バリデータ
- キー入力支援
- テストダブル
- ハイブリッドアプリケーション
- 継続的インテグレーション
iOSにおけるclickイベントの発生条件
※ iOS 7.1.1 (iPad Air 2014) での確認結果です。
iOS(iPhone,iPadなど)のSafariで、要素をタッチしてもclickイベントが発生しない場合があります。
iOSにおけるclickイベントの挙動を調査した結果は以下の通りです。
- div、span、strongなどHTMLで通常クリック対象になるような意味を持たない要素に対しては、通常clickイベントは発生しない
- button要素、href属性の設定されているa要素など、クリックされることが通常期待される要素ではclickイベントは常に発生する
- このため、documentあるいはbody要素にclickイベントハンドラをバインドしてもイベントハンドラは動作しない
- ただし、bodyより下の子孫要素にイベントハンドラをセットした場合、その要素及びその子孫要素ではclickイベントが発生するようになる
- この場合(bodyより下の要素についてclickイベントが発生するようになっている場合)、クリックしてイベントが発生すれば、bodyやdocumentにもclickイベントはバブリングする
この挙動が原因で、iOSのSafariでdocument、bodyに対してjQueryのdelegate()でclickイベントにバインドした時、バインドしたclickイベントハンドラが動作しない場合があります。
以下、ソースコード例です。
// iOSだとt1クラス要素をタッチしても、ハンドラが動作しない
$(document).delegate('.t1', 'click', fn);
// bodyに".t2"のセレクタでハンドラをバインド
// iOSだとt2クラス要素をタッチしても、ハンドラが動作しない
$('body').delegate('.t2', 'click', fn);
hifiveを使用している場合においても、イベントターゲットにグローバルセレクタを指定、またはコントローラのルートエレメントがbodyの場合に、clickにバインドしたイベントハンドラが意図通りに動作しない場合があります。
__name:'controller1',
'{.click-target} click': function(){
// グローバルセレクタ指定なので$(document).delegate()でバインドされる
// iOSで、意図通りにイベントハンドラが動作しない
}
});
h5.core.controller('body', {
__name:'controller1',
'.click-target click': function(){
// ルートエレメントがbodyなので、$(document.body).delegate()でバインドされる
// iOSで、意図通りにイベントハンドラが動作しない
}
});
ただし、delegateではなく、bind()でバインドした場合はイベントハンドラは動作します。
また、タッチした要素がaタグやbuttonタグの場合は、clickイベントが発火し、イベントハンドラが動作します。
回避方法
下記のいずれかの場合でこの問題を回避できます
delegate先をbodyの子孫要素にする
この問題はbodyまたはdocumentにdelegateした場合に起きる問題なので、bodyの中にある要素(子孫要素)にdelegateした場合、イベントハンドラは意図通りに動作します。
hfiiveを使用している場合、コントローラのバインド先がbodyである必要のない場合(body直下に全体を囲む要素がある場合など)は、コントローラのバインド先をbody内の要素にすればこの問題を回避できます。
cursor:pointerの指定
cssでcursor:pointerを指定すると、その要素をタッチした時にclickイベントが発生するようになります。
.t1{
cursor:pointer;
}
</style>
...
<div class="t1"></div>
onclick=""の指定
onclick=""が指定してある要素について、その要素をタッチした時にclickイベントが発生するようになります。
また、js側でclickハンドラを設定した場合も、HTMLにonclick=""と書いた時と同様、clickイベントが発生するようになります。
$(document).delegate('.t1', 'click', fn); // ハンドラは動作する
touchstartによる回避
"click"イベントではなく、"touchstart"イベントに対してイベントをバインドした場合、ハンドラは動作します。
bind()を使ってバインド
delegateを使わずに、bind()を使ってバインドするとイベントハンドラは動作します。ただし、bind()はdelegate()とは違い、動的に追加される要素についてはイベントハンドラは動作しなくなります。 要素が動的に追加されることが無い場合に限って、この対策が有効です。動的に追加される要素に対してもイベントハンドラを実行させたい場合は、別の方法を検討してください。
$('.target').bind('click', fn); // ハンドラは動作する
// bind()はdelegateとは違い、バインド時に存在する要素のみにイベントハンドラがバインドされます。
hifiveのコントローラでは、イベントハンドラの記法で強制的にbind()でバインドさせることができます。
__name:'controller',
'.target [click]': function(){
// イベント名を"[]"で囲って記述することで、bind()メソッドによるバインドになります
// ただし、このコントローラをバインドした時に存在する$('.target')要素のみにバインドされます。
}
});