雑記ブログ(仮)

アウトプットの練習。書きたいこと書く。

【HTML・CSS・JavaScrip】自力でプルダウンメニュー作ってみた

こんにちは、ミツ(仮)です。

最近ほんのちょっとフロントの勉強をしてて、その理解を深めるために自力でプルダウンメニューを作ってみました。

f:id:mitu00:20210316230806g:plain:w500

どういう思考回路で作ったのかっていうのと、その中でハマった場所や初めて知ったことを紹介していけたらなと思います。

この記事の「自力でプルダウンメニュー作ってみた」の「自力」ってどういうこと?って話なんですが、ここではググる時に「プルダウンメニュー」という単語を使わないという意味です。

例えば「css プルダウンメニュー 作り方」とかそういうのはなしです。 答えを見ちゃうとそれ以外にやり方が浮かばなくなってしまうので。

ステップ1:ホバーされた状態の表示を作る

f:id:mitu00:20210318005835p:plain:w500

まず、HTMLとCSSでホバーされた状態の表示、つまり最初から子メニューが出てて、どこをホバーしても何も動かない状態を目指します。

ここはあまり沼にハマることなくサクサクできました。

初知りの情報としてはli要素(箇条書き)を横並びにするときはdisplayをinline-blockにすると簡単ってことですね。 HTMLの要素って実はブロック要素とインライン要素に分かれてて、どちらも特性を持ってます。

そのうちの一つでブロック要素はwidthやpaddingで余白の調整ができるんですけど、インライン要素は余白の調整ができないんですよね、勝手に中身のサイズに合わせちゃって。

そこで、インライン要素の性質を持ちつつ余白を調節できるようにしたのがインラインブロック要素、らしいです。

それとインライン要素の特性として要素が横に並ぶらしいです。なので今回は余白も調節したいし、横並びにしたいので以下を記述したって感じですね。

display: inline-block;

ステップ2:ホバーされた時に子メニューが出るようにする

土台はできたので次はJavaScriptで動きをつけます。

どう動きをつけるかっていうと最初は親メニューのみ表示されていて、親メニューをホバーしたとき子メニューが出てくるようにします。

f:id:mitu00:20210318013002g:plain:w500

jQueryで要素を追加できることは知っていたので、これも比較的簡単でした。 初知りの情報としてはやはり.hover()ですかね。これはマウスを要素に重ねた時と外した時をトリガーに処理を実行してくれる関数です。 書式はこんな感じ。

$('セレクタ').hover(
        function () {
            //マウスが重なった時の処理
        },
        function () {
            //マウスが外れた時の処理
        });

これがわかってしまえばステップ2も難なくできました。

ステップ3:子メニューを消す

問題のステップ3。ずっと子メニューが出っぱなしだと困りますからマウスを外したら子メニューを消す処理を追加します。ここで大ハマりしました。

さっき貼った書式に従って書けばいいじゃん!って思うじゃないですか、僕もそう思ってやってみたんですけどダメだったんですね〜

もう少し具体的にいうとこんな感じのコードを書いたんですけど

parentMenu.hover(
        function () {
            $('.parentMenu-container').after(childMenu);
        },
        function () {
            $('.childMenu').remove();
        });

これだと親メニューから子メニューにマウスを動かした場合も親メニューからマウスが外れた瞬間に子メニューが消え去ってしまうんですね。

f:id:mitu00:20210318015351g:plain:w500

ほんとにはまりました。.hover()をネストで書けばいいじゃないかとか、別のホバー関数ならいけるんじゃないかとか、クラスを追加してまとめて判定すればいいんじゃないかとか仕事終わりの回ってない頭で必死に考えました。

が、全部ダメで二日ぐらい進捗なし。この時ばっかりは答えを見たくなりました。 やっぱり疲れた頭でグダグダ考えてたのがいけないと思い、一回落ち着いて整理してみました。

結局親メニューから外れた時に子メニューにホバーされていたら消したくないんだよな

→つまり子メニューにホバーされているかどうか判定できればできそうだ

→「jquery ホバー 判定」検索

→ jQuery(":hover")

なんとjQuery(":hover")で現在ホバーされている要素を取得できると。 これがわかってからは早かったです。jQuery(":hover")は重なってる要素も全てホバー判定するので箇条書きだったらliやulなど複数のホバーされている要素を取得します。

ので、.each()で回して子メニューが含まれていない場合は.remove()すればいいですね。ほんとは.inArray()とか使いたかったんですけどうまく動きませんでした泣

parentMenu.hover(
        function () {
            $('.parentMenu-container').after(childMenu);
        },
        function () {
            let hoverChildMenu = false;
            jQuery(':hover').each(function(index, element) {
                if(element.className == 'childMenu') {
                    hoverChildMenu = true;
                }
            })
            if(!hoverChildMenu){
                $('.childMenu').remove();
            }
        });

ステップ3の最終形はこんな感じ。

f:id:mitu00:20210318021657g:plain:w500

ステップ4:アニメーションをつける

ステップ3の時点でもいいっちゃいいんですけど、ちょっと物足りないなと。そう思ったので、ホバーされた瞬間「バッ!」って感じじゃなくて「シュン!」って感じになるようにアニメーションを最後につけたいと思います。

ググってみると、cssでアニメーションをつける方法の一つとしてanimationプロパティと@keyframesを使う方法があることを知りました。

簡単に説明すると@keyframesで変化させるcssの「プロパティ」と「その値」を設定して関数のようなものを作ります。

そしてその指定した動きをつけたい要素のスタイルとして、animationプロパティで先ほど作った「関数」と「アニメーションの設定(秒数とか回数とか)」するとアニメーションがつきます。

具体的にいうと@keyframesはこんな感じで使います。

@keyframes slideIn {
    0% {
        opacity:0;
        transform: translateY(-30px);
    }
    100% {
        opacity:1;
        transform: translateY(0px);
    }
}

@keyframesの横の「slideIn」というのが関数名みたいなものです。で、そのかっこの中で0%と100%という風に動き始める前のプロパティ値と動き終わりのプロパティ値を設定します。この差分がアニメーションとして動くわけです。今回はスライドインさせたいので透過度とY座標をいじっています。

で、この定義した「slideIn」をanimationプロパティでこのように指定します。

.move {
    animation: slideIn 600ms ease 0s;
}

指定しているプロパティ値の意味なんですけど、左から「@keyframesで定義した関数名」「0%〜100%に変化するまでの秒数」「アニメーションの進行度合い」「アニメーションが始まる時間」を表しています。他にも4つほどアニメーションの設定が可能です。これに関してはこちらの記事がびっくりするほどわかりやすかったです。

【CSS3】@keyframes と animation 関連のまとめ - Qiita

最後にこうしてできたスタイル.moveをホバーされたら子メニューの要素に追加する、という処理を、ステップ2、3で書いた.hover()に記述(4行目)ことで、子メニューが「シュン!」って感じで出てきてくれるようになります。

parentMenu.hover(
        function () {
            $('.parentMenu-container').after(childMenu);
            $('.childMenu li').toggleClass('move');
        },
        function () {
            let hoverChildMenu = false;
            jQuery(':hover').each(function(index, element) {
                if(element.className == 'childMenu') {
                    hoverChildMenu = true;
                }
            })
            if(!hoverChildMenu){
                $('.childMenu').remove();
            }
        });

f:id:mitu00:20210316230806g:plain:w500

終わり

今回「自力で」プルダウンメニュー作ってみたわけなんですけど、まあ大変でしたね。 まだまだ知らないことたくさんあるなと、もっと勉強しなきゃなと思いました。

ただ今回少しでも知らないを知ってるに変えることができてよかったと思います。ホバーやアニメーションとか今後使いたい場面多そうです。

また、完成度的にはとても満足してます。自分で作ったとは思えないですね。よくやったと自分を褒めてあげたいです。普通のサイトにあってもおかしくなさそうですよね? ね?笑

とにかく完成してよかった!

最後まで読んでいただきありがとうございました。

ソースコード

GitHub - mikata15431/dropdown-menu: 自作ドロップダウン