CSSだけで作るドロップダウンメニュー(多階層)

CSSだけで作るドロップダウンメニュー(多階層)

今回はグローバルメニューの登竜門、ドロップダウンメニューをCSSだけで作っていきたいと思います。「transition」でアニメーションさせつつ、ひ孫階層まで対応したコードになります。

「なるべくコードはシンプルにわかりやすく」を意識して書いて行きます。細かく分けて解説入れながらやっていきますので、「うんちくなんいかいらない!」という方は、最後までジャンプしてください。最後に全てのコードを記載します。
 

親階層

まずは親階層です。

HTML

<ul class="gnav">
    <li><a href="">Menu1</a></li>
    <li><a href="">Menu2</a></li>
    <li><a href="">Menu3</a></li>
    <li><a href="">Menu4</a></li>
    <li><a href="">Menu5</a></li>
</ul>

全体をラップする「ul」にクラス名「gnav」を付けておきます。 

CSS

.gnav {
    display: flex;
    height: 2rem;
    margin: 0 auto;
    width: 1000px;
}
.gnav > li {/*親階層のみ幅を25%にする*/
    width: 25%;
}
/*全てのリスト・リンク共通*/
.gnav li {
    list-style: none;
    position: relative;
}
.gnav li a {
    background: #001b34;
    border-right: 1px solid #eee;
    color: #fff;
    display: block;
    height: 2rem;
    line-height: 2rem;
    text-align: center;
    text-decoration: none;
    width: 100%;
}

3行目

「gnav」に高さを設定しています。一見、特に高さの指定は必要なさそうですが、こちらを指定していないと、下層の「li」にマウスオーバーした際に、意図しないところでマウスオーバーが外れますので指定しておきましょう。

7行目

親階層の「li」のみ幅25%にするため子セレクタ「>」で指定します。

11行目〜

11行目以降は全ての「li」・「a」の共通スタイルになります。

ここまでのサンプルがこちらです。

子階層

では続いて、子階層です。

HTML

<ul class="gnav">
    <li>
        <a href="">Menu1</a>
        <ul>
            <li><a href="">Child1</a></li>
            <li><a href="">Child2</a></li>
            <li><a href="">Child3</a></li>
            <li><a href="">Child4</a></li>
            <li><a href="">Child5</a></li>
        </ul>
    </li>
    <li><a href="">Menu2</a></li>
    <li><a href="">Menu3</a></li>
    <li><a href="">Menu4</a></li>
    <li><a href="">Menu5</a></li>
</ul>

子階層のリストを追加しています。追加する位置に注意しましょう。「li」の中で、「a」の直後に追加していきます。

CSS

/*子階層以降共通*/
.gnav li li {
    height: 0;
    overflow: hidden;
    transition: .5s;
}
.gnav li li a {
    border-top: 1px solid #eee;
}
.gnav li:hover > ul > li {
    height: 2rem;
    overflow: visible;
}

CSSは親階層のコードの続きから記載します。

冒頭わかりやすく、と書きましたが、ここが若干ややこしいのは否めないので頑張りましょう。ぶっちゃけコピペで動くので理解は後から(しなくても)でもOKです。

27行目

セレクタを「.gnav li li」とすることで、子階層以降全ての「li」が対象になります。そして「overflow: hidden;」、「height: 0;」とすることで、子階層以降全ての「li」が「自分からはみ出した部分は表示しない」かつ「高さは0」なので、全て非表示になります。

また「transition: .5s;」を指定し、0.5秒でアニメーションさせるようにします。

35行目

「li:hover」でマウスオーバーした際のスタイルを指定していますが、ポイントはセレクタです。

「.gnav li:hover」の部分ですが「.gnav」と「li」の間には子セレクタ「>」を入れません。これで「.gnav」内の全ての「li」にマウスオーバーした場合が対象範囲になります。

そして「:hover」以降は逆に「li:hover > ul > li」として、子セレクタ「>」を間に入れているのが今回のコードの最大のポイントです。

こうすることで「li」にマウスオーバーした直下の「li」のみが対象範囲になります。つまり、子階層以降の「li」をマウスオーバーで順番に開いていくことができます。

と言ってもまだHTMLは子階層までしか追加していないので、更に追加して見ていきましょう。

ここまでのサンプルがこちらです。

孫・ひ孫階層

孫・ひ孫階層はやること一緒なので、HTMLを一気に追加します。

HTML

<ul class="gnav">
    <li>
        <a href="">Menu1</a>
        <ul>
            <li>
                <a href="">Child1</a>
                <ul>
                    <li>
                        <a href="">Grandchild1</a>
                        <ul>
                            <li><a href="">Greatgrandchild1</a></li>
                            <li><a href="">Greatgrandchild2</a></li>
                            <li><a href="">Greatgrandchild3</a></li>
                            <li><a href="">Greatgrandchild4</a></li>
                            <li><a href="">Greatgrandchild5</a></li>
                        </ul>
                    </li>
                    <li><a href="">Grandchild2</a></li>
                    <li><a href="">Grandchild3</a></li>
                    <li><a href="">Grandchild4</a></li>
                    <li><a href="">Grandchild5</a></li>
                </ul>
            </li>
            <li><a href="">Child2</a></li>
            <li><a href="">Child3</a></li>
            <li><a href="">Child4</a></li>
            <li><a href="">Child5</a></li>
        </ul>
    </li>
    <li><a href="">Menu2</a></li>
    <li><a href="">Menu3</a></li>
    <li><a href="">Menu4</a></li>
    <li><a href="">Menu5</a></li>
</ul>

階層が深くなってややこしくなってきましたが、やっていることは子階層と同じで「li」の中の「a」の直後に追加していきます。

CSS

/*孫階層以降共通*/
.gnav li ul li ul {
    left: 100%;
    position: absolute;
    top: 0;
    width: 100%;
}

新たな追加はこれだけです。こちらも「li」や「ul」が並んでややこしいですが、子セレクタ「>」をつけていないので、孫階層以降全ての「ul」が対象範囲です。

子階層に「li」は親階層の「li」の下に追加すればOKでしたが、孫階層の「li」以降は下ではなく横に追加します。ですので「position: absolute;」を指定し、「left: 100%;」、「top: 0;」に位置を指定します。これで子階層の「li」の真横に並びます。

「position: absolute;」を指定しましたので、「width: 100%;」も忘れずに! 

ここまでのサンプルがこちらです。

これで子→孫→ひ孫と順番に開きました!

一番右のドロップダウンメニューは逆方向へ折り返す

まだ問題があります。これまで一番左のメニューにドロップダウンメニューを追加してきましたが、一番右のメニューにドロップダウンメニューを追加したらどうなるでしょうか?

狭い画面で見ているとブラウザからはみ出していきますね。。。ですので一番右のドロップダウンメニューは逆方向へ折り返すようにします。

HTML

HTMLは変化なしです。

CSS

/*一番右のメニュー*/
.gnav > li:nth-child(5) ul li ul {
    left: -100%;
}

これだけです。これで一番右「.gnav > li:nth-child(5)」のドロップダウンメニューは右に折り返します。 

ここまでのサンプルがこちらです。

ドロップダウンメニューを装飾

さてもう少しです。これでひ孫階層までは順番にアニメーションで動くようにできました。しかしぶっちゃけ使いづらいドロップダウンメニューです。なぜかと言うと、どこにマウスオーバーすれば下層のメニューが開くかわからないからですね。

ですので、最後にユーザービリティを考慮して、下層のドロップダウンメニューがあるところには▶︎をつけて、更にマウスオーバーした際の背景色の変化も階層ごとに分けてつけていきたいと思います。

HTML

HTMLは変化なしです。

CSS

まずは「a」の背景色から。

/*aの背景色を指定*/   
.gnav li:hover a {/*親階層*/
    background: #00305c;
}
.gnav li li a {/*子階層*/
    background: #00305c;
}
.gnav li li:hover a {
    background: #004789;
}
.gnav li ul li ul li a {/*孫階層*/
    background: #004789;
}
.gnav li ul li ul li:hover a {
    background: #0065c1;
}
.gnav li ul li ul li ul li a {/*ひ孫階層*/
    background: #0065c1;
}
.gnav li ul li ul li ul li:hover > a {
    background: #1e7dd4;
}

背景色は1つ上の階層の「a」にマウスオーバーした際の色に合わせて、階層が深くなるごとに少しずつ彩度を上げています。 

ここまでのサンプルがこちらです。

これだけでもかなりわかりやすくなってきましたね。更に▶︎をつけます。

/*リストに「▶︎」を付ける*/
.gnav > li > ul:before{/*子階層*/
    border: 5px solid transparent;
    border-top: 5px solid #fff;
    content: "";
    right: 1rem;
    position: absolute;
    top: 1rem;
    transform: translateY(-40%);
}
.gnav li ul li ul:before {/*孫階層*/
    border: 5px solid transparent;
    border-left: 5px solid #fff;
    content: "";
    left: -20px;
    position: absolute;
    top: 1em;
    transform: translateY(-50%);
}
.gnav li:nth-child(5) ul li ul:before {/*一番右のメニューの孫階層*/
    border: 5px solid transparent;
    border-right: 5px solid #fff;
    left: auto;
    right: -20px;
}

73行目

▶︎については「ul」の擬似要素でつけます。子階層は▶︎の向きが下向きになるので、子セレクタ「>」で指定しています。

91行目

一番右の「li」は▶︎の向きを左向きにします。

WordPressで制作する際は、子階層を持っているメニューには「.has-children」みたいなクラスを自動でつけてくれるので、そのクラスの擬似要素でやってました。

しかし、せっかく今回ドロップダウンメニューの記事を書くので、ググりまくって先人の皆様のお知恵を拝見したところ「なるほどね!」となる新たな発見がありました。

子階層を持っているメニューに直接▶︎をつけるのではなく、子階層のメニューの側から1つ上の階層のメニューのところへ▶︎を配置するという方法です。

確かにこれなら子階層を持っているメニューに、特にクラスをつける必要もなく、▶︎をつけることができます。

ここまでのサンプルがこちらです。

できましたね。お疲れ様です。長い内容にお付き合いいただきありがとうございました。あとはおまけ的な内容だけです。

おまけ!高さじゃなくて透明度をアニメーションさせるドロップダウンメニュー

せっかくなので、フワっとフェードインするタイプのドロップダウンメニューもやってみました。

CSS

CSSの変更箇所です。

/*子階層以降共通*/
.gnav li li {
    height: 0;
    opacity: 0;
    overflow: hidden;
    transition: opacity .5s;
}
.gnav li li a {
    border-top: 1px solid #eee;
}
.gnav li:hover > ul > li {
    height: 2rem;
    opacity: 1;
    overflow: visible;
}
.gnav li ul li ul {
    left: 100%;
    position: absolute;
    top: 0;
    width: 100%;
}

29行目

透明度をアニメーションさせるので、「opacity: 0;」にします。 

31行目

「transition: opacity .5s;」でアニメーションは透明度のみに指定します。

38行目

マウスオーバーした際に「opacity: 1;」で表示します。

透明度をアニメーションさせるサンプルがこちらです。

今回のすべてのコード

解説では省いていたドロップダウンメニュー以外の部分も載せます。

HTML

<body>
    <div class="container">
        <h1 class="title">CSSだけで作るドロップダウンメニュー(多階層)サンプル7</h1>
        <p class="read">
            「transition」でアニメーションさせつつ、第4階層(ひ孫階層)まで対応したコードになります。一番右の「Menu5」は逆方向に折り返すようにしています。
        </p>
        <ul class="gnav">
            <li>
                <a href="">Menu1</a>
                <ul>
                    <li>
                        <a href="">Child1</a>
                        <ul>
                            <li>
                                <a href="">Grandchild1</a>
                                <ul>
                                    <li><a href="">Greatgrandchild1</a></li>
                                    <li><a href="">Greatgrandchild2</a></li>
                                    <li><a href="">Greatgrandchild3</a></li>
                                    <li><a href="">Greatgrandchild4</a></li>
                                    <li><a href="">Greatgrandchild5</a></li>
                                </ul>
                            </li>
                            <li><a href="">Grandchild2</a></li>
                            <li><a href="">Grandchild3</a></li>
                            <li><a href="">Grandchild4</a></li>
                            <li><a href="">Grandchild5</a></li>
                        </ul>
                    </li>
                    <li><a href="">Child2</a></li>
                    <li><a href="">Child3</a></li>
                    <li><a href="">Child4</a></li>
                    <li><a href="">Child5</a></li>
                </ul>
            </li>
            <li><a href="">Menu2</a></li>
            <li><a href="">Menu3</a></li>
            <li><a href="">Menu4</a></li>
            <li>
                <a href="">Menu5</a>
                <ul>
                    <li>
                        <a href="">Child1</a>
                        <ul>
                            <li>
                                <a href="">Grandchild1</a>
                                <ul>
                                    <li><a href="">Greatgrandchild1</a></li>
                                    <li><a href="">Greatgrandchild2</a></li>
                                    <li><a href="">Greatgrandchild3</a></li>
                                    <li><a href="">Greatgrandchild4</a></li>
                                    <li><a href="">Greatgrandchild5</a></li>
                                </ul>
                            </li>
                            <li><a href="">Grandchild2</a></li>
                            <li><a href="">Grandchild3</a></li>
                            <li><a href="">Grandchild4</a></li>
                            <li><a href="">Grandchild5</a></li>
                        </ul>
                    </li>
                    <li><a href="">Child2</a></li>
                    <li><a href="">Child3</a></li>
                    <li><a href="">Child4</a></li>
                    <li><a href="">Child5</a></li>
                </ul>
            </li>
        </ul>
    </div>
</body>

CSS(高さのアニメーション)

「height」をアニメーションさせる方です。

*{
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}
body{
    /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#99e379+0,7db9e8+100 */
    background: #99e379; /* Old browsers */
    background: -moz-linear-gradient(left,  #99e379 0%, #7db9e8 100%); /* FF3.6-15 */
    background: -webkit-linear-gradient(left,  #99e379 0%,#7db9e8 100%); /* Chrome10-25,Safari5.1-6 */
    background: linear-gradient(to right,  #99e379 0%,#7db9e8 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#99e379', endColorstr='#7db9e8',GradientType=1 ); /* IE6-9 */
    font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", YuGothic, "ヒラギノ角ゴ ProN W3", Hiragino Kaku Gothic ProN, Arial, "メイリオ", Meiryo, sans-serif;
    font-size: 16px;
}
.container{
    padding-top: 4rem;
}
.title{
    color: #fff;
    margin-bottom: 2rem;
    text-align: center;
}
.read{
    color: #fff;
    line-height: 1.6;
    margin:0 auto 2rem;
    width: 800px;
}
 
.gnav {
    display: flex;
    height: 2rem;
    margin: 0 auto;
    width: 1000px;
}
.gnav > li {/*親階層のみ幅を25%にする*/
    width: 25%;
}
/*全てのリスト・リンク共通*/
.gnav li {
    list-style: none;
    position: relative;
}
.gnav li a {
    background: #001b34;
    border-right: 1px solid #eee;
    color: #fff;
    display: block;
    height: 2rem;
    line-height: 2rem;
    text-align: center;
    text-decoration: none;
    width: 100%;
}
/*子階層以降共通*/
.gnav li li {
    height: 0;
    overflow: hidden;
    transition: .5s;
}
.gnav li li a {
    border-top: 1px solid #eee;
}
.gnav li:hover > ul > li {
    height: 2rem;
    overflow: visible;
}
.gnav li ul li ul {
    left: 100%;
    position: absolute;
    top: 0;
    width: 100%;
}
/*一番右のメニュー*/
.gnav > li:nth-child(5) ul li ul {
    left: -100%;
}
/*aの背景色を指定*/   
.gnav li:hover a {/*親階層*/
    background: #00305c;
}
.gnav li li a {/*子階層*/
    background: #00305c;
}
.gnav li li:hover a {
    background: #004789;
}
.gnav li ul li ul li a {/*孫階層*/
    background: #004789;
}
.gnav li ul li ul li:hover a {
    background: #0065c1;
}
.gnav li ul li ul li ul li a {/*ひ孫階層*/
    background: #0065c1;
}
.gnav li ul li ul li ul li:hover a {
    background: #1e7dd4;
}
/*リストに「▶︎」を付ける*/
.gnav > li > ul:before{/*子階層*/
    border: 5px solid transparent;
    border-top: 5px solid #fff;
    content: "";
    right: 1rem;
    position: absolute;
    top: 1rem;
    transform: translateY(-40%);
}
.gnav li ul li ul:before {/*孫階層*/
    border: 5px solid transparent;
    border-left: 5px solid #fff;
    content: "";
    left: -20px;
    position: absolute;
    top: 1em;
    transform: translateY(-50%);
}
.gnav li:nth-child(5) ul li ul:before {/*一番右のメニューの孫階層*/
    border: 5px solid transparent;
    border-right: 5px solid #fff;
    left: auto;
    right: -20px;
}

CSS(透明度のアニメーション)

「opacity」をアニメーションさせる方です。先ほど解説した通り、数行しか変更してないですが、こちらも全部載せます。

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}
body{
    /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#99e379+0,7db9e8+100 */
    background: #99e379; /* Old browsers */
    background: -moz-linear-gradient(left,  #99e379 0%, #7db9e8 100%); /* FF3.6-15 */
    background: -webkit-linear-gradient(left,  #99e379 0%,#7db9e8 100%); /* Chrome10-25,Safari5.1-6 */
    background: linear-gradient(to right,  #99e379 0%,#7db9e8 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#99e379', endColorstr='#7db9e8',GradientType=1 ); /* IE6-9 */
    font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", YuGothic, "ヒラギノ角ゴ ProN W3", Hiragino Kaku Gothic ProN, Arial, "メイリオ", Meiryo, sans-serif;
    font-size: 16px;
}
.container{
    padding-top: 4rem;
}
.title{
    color: #fff;
    margin-bottom: 2rem;
    text-align: center;
}
.read{
    color: #fff;
    line-height: 1.6;
    margin:0 auto 2rem;
    width: 800px;
}
     
.gnav {
    display: flex;
    height: 2rem;
    margin: 0 auto;
    width: 1000px;
}
.gnav > li {/*親階層のみ幅を25%にする*/
    width: 25%;
}
/*全てのリスト・リンク共通*/
.gnav li {
    list-style: none;
    position: relative;
}
.gnav li a {
    background: #001b34;
    border-right: 1px solid #eee;
    color: #fff;
    display: block;
    height: 2rem;
    line-height: 2rem;
    text-align: center;
    text-decoration: none;
    width: 100%;
}
/*子階層以降共通*/
.gnav li li {
    height: 0;
    opacity: 0;
    overflow: hidden;
    transition: opacity .5s;
}
.gnav li li a {
    border-top: 1px solid #eee;
}
.gnav li:hover > ul > li {
    height: 2rem;
    opacity: 1;
    overflow: visible;
}
.gnav li ul li ul {
    left: 100%;
    position: absolute;
    top: 0;
    width: 100%;
}
/*一番右のメニュー*/
.gnav > li:nth-child(5) ul li ul {
    left: -100%;
}
/*hover*/  
.gnav > li:hover > a {/*親階層*/
    background: #00305c;
}
     
.gnav > li li > a {/*子階層*/
    background: #00305c;
}
.gnav > li li:hover > a {
    background: #004789;
}
 
.gnav li ul li ul li > a {/*孫階層*/
    background: #004789;
}
.gnav li ul li ul li:hover > a {
    background: #0065c1;
}
 
.gnav li ul li ul li ul li > a {/*ひ孫階層*/
    background: #0065c1;
}
.gnav li ul li ul li ul li:hover > a {
    background: #1e7dd4;
}
 
/*リストに「▶︎」を付ける*/
.gnav > li > ul:before{/*子階層*/
    border: 5px solid transparent;
    border-top: 5px solid #fff;
    content: "";
    right: 1rem;
    position: absolute;
    top: 1rem;
    transform: translateY(-40%);
}
.gnav li ul li ul:before {/*孫階層*/
    border: 5px solid transparent;
    border-left: 5px solid #fff;
    content: "";
    left: -20px;
    position: absolute;
    top: 1em;
    transform: translateY(-50%);
}
.gnav li:nth-child(5) ul li ul:before {/*一番右のメニューの孫階層*/
    border: 5px solid transparent;
    border-right: 5px solid #fff;
    left: auto;
    right: -20px;
}

まとめ

このブログをやっていなかったら、まず「CSSだけで作るドロップダウンメニュー」なんて改めて振り返ることはなかったと思いますが、「楽勝でしょ!」と思ってやってみると意外と詰まったり、いろいろ調べるうちに新たな発見がありました。特に子階層を持っている場合のアイコンの付け方は先人の皆様の知恵に感謝です。

また、「ul」、「li」が多階層でいくつも連なるので、「このセレクタでの影響範囲はここまでだな」という感じで、CSSのセレクタを徹底的に意識して指定していかないと思わぬ挙動になります。これはかなり脳の筋トレになりました。。。

ここまで深い階層のドロップダウンメニューは正直あまりないかもしれませんが、孫階層までは十分実用性があります。最後のコードをコピペすれば動きますので、この記事を参考に、いろいろやってみてくださいね。

Category&Tag