かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

iOS javascript clickイベントが効かないにはまる。

レスポンシブなサイトを作成していてモバイルの時によくある、メニューアイコンを押したら全画面にメニューが表示されてメニュー以外をクリックするとメニューが閉じる機能を作っていました。
Chromeの開発ツールでは意図したとおりに動作していたのですが、iPhone実機で確認した所メニュー以外の部分をタップしたらメニューを閉じる機能が動作していませんでした。

確認環境

clickイベントが効かないパターンがあるっぽい

動作しなかった例

$(document).on('click', 'div.hitarea', function(){ /*... 処理 */ });

※ 対象の要素を解りやすくするために敢えてdiv.hitareaと書いています

どうやら$(document).on$('body').on のようなイベントの登録のしかたで、イベント対象がaタグではない時にクリックイベントが無視されてしまうようです。

解決方法

1. クリック対象となる要素にcursor: pointerというスタイルを付ける

クリック対象の要素にcursor: pointerというスタイルがあれば、aタグでなくてもクリックイベントが効くようになるようです。(イベントがdocumentやbodyといったものに登録されている場合イベントターゲットがpointerな要素ならタップ可能な要素だとiOSが判定するような仕組みっぽいです。)

今回の例だと次のようにスタイルを加えるとクリックイベントが動作するようになりました。

div.hitarea {
  cursor: pointer;
}

ボタンのように使いたい機能だった場合はこの方法が簡単です。
しかし、今回のようなレスポンシブなサイトで、ボタンエリア以外をクリックしたらメニューを閉じたいというような場合、モバイルで見てる時は良いのですがPCで見るとモーダルのメニュー上どこにマウスを持っていってもカーソルがクリック可能なポインターになってしまうので、ちょっと問題がありました。

2. document, body にイベントを登録しなければOK

iOSでクリックイベントが動作しないのは、documentまたはbodyにイベントが登録されている かつ 対象の要素がaタグでない(pointerのスタイルがない) とき ですので、$(document).on$('body').onとしなければ問題なく動作します。

例えば次のようなHTML構造で

<body>
  <div class="container">
    <div class="hitarea"></div>
  </div>
</body>

単純に最初から画面にある該当要素だけでクリックイベントが効けば良いのであれば

$(document).on('click', 'div.hitarea', function(){ /*... 処理 */ });

ではなく

$('div.hitarea').on('click', function(){ /*... 処理 */ }); 

とすればOKです。

後から追加される要素にもクリックイベントが効く必要があるときは、

$('.container').on('click', 'div.hitarea', function(){ /*... 処理 */ }); 

上記のようにdocumentbodyではなく対象となる要素が追加される親要素にイベント登録をすれば、後から追加されるdiv.hitarea要素にもクリックイベントが効くようになります。

 
Chromeの開発ツールで見た目と動作問題ないなーと思っても実機だと挙動が異なることがあるってのを改めて実感しました。(iOSで見てたブラウザもデフォルトのSafariChromeとも異なりますからそりゃ違いもありますよねw)
モバイルサイト製作時はめんどくさがらずに、ケーブル繋いで実機のブラウザで見て確認・デバックをしなきゃなーって思いました。(でも、ちょっとめんどくさい...

過去にaタグだけどclickイベントが効かない事があったのですが、href属性を付けていなかったので、cursor: poinrerのスタイルが無い扱いになってたから、動作しなかったんじゃないかなって今回の件でナゾが判明した気がしました。


[参考]