親カテゴリのドロップダウン子カテゴリのドロップダウンエリア外をタップすると閉じるJS全体レスポンシブなドロップダウンメニューのDEMOおわりにドロップダウンメニューって?メニューをクリックしたりマウスを上に載せたりすることでそのメニューの下に出てくるようなメニューのことです。ドロップダウンメニューの例ドロップダウンメニューの実装方法はググると結構出てきます。jQueryプラグインとかをこのためだけに読み込ませたりするのはなんか違う気がするし、「CSSだけでできる!」とかいうのもありますが、実際は動いてもデザインが思い通りにならなかったり、タブレットで全然使い物にならなかったり、なんてことがありますね。僕なんかは特にそうですが、ほぼ「コーディング専業」の人は依頼されたデザインの合わせて自由自在に変えられるようにこのくらいのUIはササっと書ける知識を持っておく必要があります。今回はそういう時に使えるメモです。言うて自分もけっこう忘れるので。基本的にはデザイナーから依頼されたデザインを完全に再現するという体で進めます。そのためCSSでデザインを忠実に再現することが前提となります。今回はデザイナーなしで自分で作りますがご容赦ください。-->cssCSSはリセットされている前提で進めます。第一段階としてPCではCSSの:hoverで動くようにだけしておきます。JSで追加するクラスのCSSに関してはその都度書いていきます。基本的にこういった様々なサイトでよく使うようなUIを作る時はCSSのみで解決できる方が好ましいと思っています。JavaScript全盛の時代ではありますが、Vueなどのフレームワークを使っても結局JSで操作してCSSやCSSの付いたクラスを付け替えることが大半なので:hoverや:focusといった擬似クラスが使える場合はそちらを優先して使った方が効率的に思います。そのためこういった小規模UIを自作で作るときは先にCSSだけで動かすことを最初に考えます。レスポンシブがスタンダードになった最近では、スマホやタブレットといったマウスのないタッチ操作デバイスの対応は違ってきます。スマホやタブレットでは逆にJSを使わないと制御できない部分が多くなります。このドロップダウンメニューもCSSのみではスマホやタブレットでうまく機能しないのでJavaScriptでのカスタマイズが必要です。最初の段階でCSSで見た目を作り込んでおくと次のJS実装がやりやすいと思います。JavaScriptでタップ操作に対応するスマホ・タブレット・PCで違う動作をするのでちょっとややこしくなりますが、分けて考えていきます。サンプルはjQuery(ES6+をBabel7でトランスパイル)で書きます。スマホスマホではタップして開くアコーディオンメニューにします。横の「+」ボタンを押すと下のカテゴリを開いたり閉じたりします。});});.js-toggleが「+」ボタンです。タップするとその直後の要素をnext()で取得し、slideToggle()を使ってメニューを開閉します。連打バグ防止のために.not(':animated')でアニメーションしていない時のみ動作するようにします。ここでは「+」ボタンはCSSでタブレットの大きさになるとdisplay:none;で消えるように設定しているので、ここでは「スマホだけ動作するような判定処理」はしません。タブレットタブレットではPCと同じデザインになりますが、動作をマウスからタップに置き換えます。1度目のタップでメニューを開いてメニューを開いた状態で同じリンクをタップするとリンクに飛ばす仕様です。メニューが開いた状態でメニュー以外をタップするとメニューを閉じます。ここはスマホでもPCで動作して欲しくない処理なので、「タブレットのみ」で動作するようにデバイス判定します。device;}親カテゴリのドロップダウンデバイス判定の関数を使って、次に親カテゴリの動作。false);touchstartはタップした瞬間に動作するイベントですが、その最初にデバイス判定してタブレット以外の場合はreturnで終了させます。タブレットの時のみ、.is-showクラスがない時につけるようにします。この時、.is-showクラスがある場合はそのままreturnで終了し、ない場合はリンクの機能を止めてドロップダウンを開きたいのでpreventDefault()を使います。stopPropagation()はイベントの伝播を止めたい場合に使います。今回はなくても動きますが一応書いています。false);});子カテゴリも親カテゴリと挙動は同じです。ドロップダウンさせたいカテゴリに.js-has-childというクラスを付けておいて、それに対して動作させます。複数要素に対してイベントを発火させる1つのカテゴリが表示されたらそれ以外のカテゴリはすべて非表示にするというところです。});documentにtouchstartイベントを追加します。これでどこをタップしてもイベントが発火する形になります。タブレット以外の時、ドロップダウンが開いていない時はすぐに終了。タップした要素(e.target)の先祖に.js-dropdownが含まれていない場合にすべてのドロップダウンを閉じます。その際にやはりpreventDefault()でリンクを機能させないようにしておきます。これでタブレットで「1度目のタップでメニューを開いてメニューを開いた状態で同じリンクをタップするとリンクに飛ばす、またメニューが開いた状態でメニュー以外をタップするとメニューを閉じる」というちょっと複雑な条件をクリアできました。Main();レスポンシブなドロップダウンメニューのDEMOレスポンシブなドロップダウンメニューのDEMOどのデバイスでも動作するドロップダウンメニューができました。デザインに関してはイケてないので参考にしないでくださいwおわりに最近よく作っているBASEデザインマーケットで販売しているテーマで、「タブレットのタップ操作がうまくいかない」といったご指摘を頂いて何度か修正しました。その時に行ったこととほとんど同じ処理なのですが、しばらくすると忘れそうなのでここに書き残しておくことにしました。こんなUI、Vueとかで作った方が早そうな気がしますね。時間がある時にVueバージョンでも作ってみます。