タップ(クリック)で開閉するメニュー

スマホ対応案件が増えてくるにつれ、ハンバーガーメニューを作らなければならない場面が増えてきました。
タップするとメニューが開く、という条件だけなら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%;のような形にしても、中のテキストの高さ分からアニメーションが始まるため、バタついて見えるという問題も。