久しぶりの更新はようやく梅雨模様になってきた仙台事務所の古川がお送りします。
今回は社内チャットワークで新人さんの質問をきっかけに話題に挙がっていた「RWDにおけるイベント登録」についてのお話しです。
はじめに
RWDのページではブレークポイントごとに異なる動作をすることがあります。例えばスマートフォン向けの解像度ではコンテンツ領域が狭いため、アコーディオンメニューを使用して一覧性を良くし、パソコン向けの解像度ではアコーディオンを解除し、見出しと本文をサクッと見れるようにする・・・といったケースです。
このとき、よほど奇抜なデザインではない限り同じHTMLを使うことが出来るのですが、アコーディオンを開く際のクリック(タップ)イベントも同じHTMLに登録されているため、すべてのブレークポイントでイベントが実行されます。
そういったブレークポイントごとにイベントの発生する・しないをどういった方法で制御するのか、そしていくつか方法があるなかでどの方法を採用するとよいのか考察をしていきます。
それではさっそく制御方法を考えてみましょう。
制御方法その1 HTMLを分ける
一番簡単な方法です。イベント登録が必要なHTMLと不要なHTMLに分け、メディアクエリを使用してHTMLの表示・非表示を切り替えます。
アコーディオンの動作が必要な場合にだけ、その動作が適用されるHTMLを表示させるという考え方です。
ただ同じコンテンツが複数存在することになるため、HTMLが増えますしメンテナンス性も悪くなるためできれば使いたくない方法です。
逆にLPなどメンテナンス性を考慮しなくてもいいページはこの方法が手軽でいいと言えます。
制御方法その2 ブレークポイントごとにイベントを登録する
必要なときに必要なイベントを登録する方法です。JavaScriptでメディアクエリと同じブレークポイントの判定をする必要があり、その判定を間違えるとCSSとJavaScriptがうまく連動せずチグハグな動作になるため注意が必要です。
JavaScriptでブレークポイントの判定はwindow.matchMediaメソッドで行いますが、IEは10からの対応となります。またwindow.innerWidthはIE9からの対応となるため、対応ブラウザを確認して使用する方法を選びましょう。
JavaScriptとメディアクエリのブレークポイントを連携する方法は以下の記事がよくまとまっていますので参考にしてください。
Media Queries(メディアクエリ)とJavaScriptの連携について
そしてJavaScriptでメディアクエリと上手く連携が出来たらお終いとはならないのがこの方法です。気づきにくい落とし穴として、イベントが登録されない問題とイベントの多重登録問題です。
それぞれケース別に見てみましょう。
イベントが登録されないケース
ここではスマートフォン向けに600px以下のときクリックイベントを登録するという想定で以下のコードを書きました。
See the Pen RWDでイベント登録がされないケース by Y.Kogawa (@Y-Kogawa) on CodePen.0
600px以下の端末・ウィンドウサイズでページを開いたときはこのコードは問題なく実行されます。
しかし800pxで開いてから600px以下になった場合にはアラートが表示されず、また600px以下で開いてから601px以上にするとアラートが表示されてしまいます(ウィンドウサイズを変えてリロードして試してみてください)。
これはイベント登録のコードがページを開いたときにしか実行されていないために起きる問題です。
この問題を解決するためにはresizeイベントやorientationイベントをキャッチして600px以下のときにイベントを登録するコードを再実行しなければいけません。
See the Pen RWDでイベント登録がされないケースを解決したように見えるケース by Y.Kogawa (@Y-Kogawa) on CodePen.0
しかし、この方法もイベントの多重登録という問題を発生させます。詳しくは次の「イベントが多重登録されるケース」で見てみましょう
イベントが多重登録されるケース
前述した通りウィンドウサイズでイベントの登録・非登録を判定する場合、ウィンドウサイズが変わったときにリサイズイベントをキャッチして再度その判定をしなければいけませんが、リサイズイベントは何度も発生するため、その度にイベントが登録され、多重登録の状態になります。
多重登録された場合、1回のクリックで複数回コードが実行されてしまうため、おかしな動作をしたり、パフォーマンスの悪化を招きます。
先ほどと同じコードですが、これを新しいウィンドウで開きしウィンドウサイズを何度も変更して(600pxになるように)からボタンをクリックしてみてください。するとアラートが複数回表示されることがわかります。
See the Pen RWDでイベント登録がされないケースを解決したように見えるケース by Y.Kogawa (@Y-Kogawa) on CodePen.0
これを解決するためには下記のようにリサイズイベントごとにクリックイベントを解除する方法が簡単です。
See the Pen RWDでイベントの多重登録を解決したケース by Y.Kogawa (@Y-Kogawa) on CodePen.0
クリックイベントを解除する位置がポイントです。if文の中でやってしまうと、パソコン版のときにイベントが解除されなくなりますので、if文の前に実行しましょう。これでイベントの多重登録も防ぎ、601pxになったときにアラートを表示させないこともできるようになりました。
このように「ブレークポイントごとにイベントを登録する」方法はイベントの登録や解除をして管理をしなくてはいけないため、非常に面倒な方法です。例ではクリックだけのシンプルなものでしたが、実際には複数のイベントが混ざりあうことも多く、そうなるととあっちは解除してこっちは解除しない。といった形で複雑化し混乱の元になります。
必要なときに必要なイベントを発生させるようにしたい。という気持ちはよくわかりますが、そのためにコードが肥大化して複雑になってしまっては本末転倒とも言えます。そのためお勧めしたいのは次の「イベント発生後にブレークポイントごとに動作を変える」方法です。
制御方法その3 イベント発生後にブレークポイントごとに動作を変える
この方法は単純明快です。イベントの登録はブレークポイントを気にせず行い、イベント発生後にブレークポイントに合わせて動作を変えるというものです。
イベントの中で判定するため、Sandbox化し他に影響を与えにくいわかりやすいコードになります。また判定文のところは関数化して使いまわせるようにしてもいいでしょう。
See the Pen RWDでイベント登録をしたあとにブレークポイントごとに動作を変えるケース by Y.Kogawa (@Y-Kogawa) on CodePen.0
個人的にはこの方法を好んで使用しています。
最後に
いかがでしたでしょうか。いくつか方法がある中で僕もはじめは「制御方法その2 ブレークポイントごとにイベントを登録する」を考え、必要なときに必要なイベントを・・・と思ってやったことがあります。しかしイベントの管理が大変になり、最終的には最後の方法に落ち着いています。
JavaScriptに慣れていない方にはよくわからない説明もあったかと思いますが、イベントの登録・非登録を管理するのは大変だよ。ということを知っていただけたなら嬉しいです。