スマホ対応案件が増えてくるにつれ、ハンバーガーメニューを作らなければならない場面が増えてきました。
タップするとメニューが開く、という条件だけならCSSだけでも可能(:hoverで開くように指定すると、スマホではタップで動作する)ですが、閉じるにはメニュー以外のところをタップしなければなりません。できれば同じところをタップで開閉したい。
これを実現しようとすると、javascriptの助けを借りざるを得ない。いろいろ調べたところjQueryで簡単に実装できることが判明したのでメモ。
コード
HTML
<ul> <li class="burg-trigger">Menu <ul class="burg-child"> <li>list-1</li> ・ ・ ・ <li>list-n</li> </ul> </li> </ul>
jQuery
<script> $(function(){ $('.burg-trigger').click(function(){ $('.burg-child').toggleClass('visible'); $(this).toggleClass('visible'); }); }); </script>
WordPressに適用する場合、$をjQueryに置き換える。
<script> jQuery(function(){ jQuery('.burg-trigger').click(function(){ jQuery('.burg-child').toggleClass('visible'); jQuery(this).toggleClass('visible'); }); }); </script>
HTMLのヘッダーでjQueryライブラリを読み込ませておくこと。
本コードはHTMLコードよりあとに記述。
CSS
.burg-trigger:hover, .burg-trigger:active, .burg-trigger:focus { cursor: pointer; } .burg-child { height: 0; overflow: hidden; } .visible { height: auto; }
解説
要件
1ページで使用されるのは1箇所(ハンバーガーメニュー)のみ。
トリガーも1ページ中1箇所のみ。
burg-triggerをタップすると、隠れているburg-childが表示され、もう一度タップすると隠れる、という動きにしたい。
なお、burg-childを隠す際、display: none; と visibility: hidden; は使用不可。(読み上げソフトで無視されるため)
なるべくシンプルに。jQuery使用可。
HTML
トリガーのli要素内にburg-childのリストを内包。
jQuery
burg-triggerのクラスをクリックすると、burg-childのクラスを持つ要素に、visibleというクラス名を追加する。もう一度クリックするとvisibleのクラス名を削除する。
CSS
burg-triggerにはa要素を入れていないが、クリック可能であることを示すため、マウスオーバー時にカーソルをポインターの形に変更する。
burg-childは通常時は高さを0に。このままでは、はみ出しているli要素が見えてしまうため、overflow:hidden;で見えなくする。
これにより、キーボード操作でも見えないリスト要素をたどることができるし、音声読み上げソフトでも正常に読み上げることができる。NVDAでしか検証していないけど、他のソフトでも多分大丈夫だと思う。
visibleクラスにheigt: auto;を設定する。
burg-childとvisibleが同時に指定されているときはvisibleクラスのheigt:auto;が優先されるように、visibleを後に書く。
今後の課題
複数箇所への対応。
jQueryを使わず、生のJavaScriptで同じことをできるようにしたい。(jQueryライブラリの読み込みによるパフォーマンス低下が無視できない場合があるため。この動作のためだけに使うにはオーバースペックな気がする。)
アニメーション効果の実装。
複数個所に対応するには
next()をつけるだけだった。
<script> jQuery(function(){ jQuery('.burg-trigger').click(function(){ jQuery(this).next('.burg-child').toggleClass('visible'); jQuery(this).toggleClass('visible'); }); }); </script>
アニメーションについて
CSSアニメーションが手っ取り早いけど、heigt:auto;に対してはどうも機能しないらしい。
また、height:0;からheight:100%;のような形にしても、中のテキストの高さ分からアニメーションが始まるため、バタついて見えるという問題も。