わたしと納豆ごはん

納豆、Web、雑記など

inputタグとか使わないCSSだけのハンバーガーメニュー

(上の画像はフリー写真素材ぱくたそさんからです)

この記事は別ブログの記事の転載、引越しした記事なのですが、

ハンバーガーメニューといったレスポンシブ対応メニューてよくありますよね。

それを設置するのにCSSだけでの場合、HTMLにinputのタグを使ったものをネットでよく見ます。

この場合、HTMLにinput等を書かなければ機能しないのですが、HTMLは適切なタグや要素を使うのが基本。

フォーム等に使われる要素であるinputをレスポンシブ用のメニューとして使うのは適切なのかどうなのか?なんていう疑問あるかと思います。

ですが、この手のメニューをCSSだけで行う場合、メニュー関係で定番の要素(navやul)だけでは出来ないんじゃないかな?とも思っておりました。

ところがですね、やはりネットは広い。このような記事を見つけました。

リンク:CSSでonclickイベントを実装する - CSS note

このような方法があったのですね。

これを上手く使えば、input タグなどを使わず、nav タグや ul タグだけを使った『CSSだけのハンバーガーメニュー』ができそうです。

それで作ってみたので、そのお披露目です。

CSSだけのハンバーガーメニュー

まずはサンプルです。

こちらのアイコンをクリックまたはタップすると、サンプルのメニューが出てきます。

↑は開閉の仕組みは同じですが、サンプル用にHTMLとCSSを若干変えています。あとスマホからだと初回は2回ほど押さないと反応しない。

それとアイコンは、はてなブログにデフォルトでインストールされているアイコンフォントを使っています。ゆえに「はてなブログ仕様」です。

サンプルページも作りました。

まず最初は、HTMLからです。

実はちょっとだけHTMLに仕掛けが必要です。

といっても、何かタグを書き足すのではなく、次のようにtabindex="-1"という属性を、以下のnavとulなどネスト状態の2か所に書き足します。

<nav tabindex="-1" class="menu">
  <ul tabindex="-1" class="menu-ul">
    <li><a href="#">リンク</a></li>
    <li><a href="#">リンク</a></li>
    <li><a href="#">リンク</a></li>
    <li><a href="#">リンク</a></li>
    <li><a href="#">リンク</a></li>
  </ul>
</nav>

このtabindex="-1"は、簡単に言えば『クリックしたらフォーカスがあたるようにする』というものです。

これを設定すると、本来フォーカスされなち要素であってもフォーカスできるようになります。また値に-1を設定すると、tabキーでのフォーカスはしなくなりますが、マウスなどクリックによるフォーカスは出来るようになります。

詳細はこちらに書かれています。ご参照ください。
developer.mozilla.org

次にCSSです。このHTMLを使ってハンバーガーメニューを実装します。

以下そのコードです。

.menu {
    position: fixed;
    top: 0;
    left: 0;
    outline: none;
    z-index: 100;
}
.menu::before {
    position: absolute;
    top: 10px;
    left: 10px;
    cursor: pointer;
    /* 
       アイコンは、はてなブログのデフォルトアイコン使用しています。
   */
    font-family: blogicon;
    content: "\f003";
    font-size: 30px;
    line-height: 1;
}
.menu-ul {
    position: absolute;
    top: 0;
    left: -80vw;
    margin: 0;
    list-style: none;
    width: 80vw;
    height: 100vh;
    background: #fff;
    padding: 20px;
    box-sizing: border-box;
    outline: none;
    transition: left 0.4s 0.1s;
}
.menu-ul::after {
    position: absolute;
    content: "";
    top: 0;
    left: 0;
    width: 0;
    height: 0;
    z-index: -1;
    background: rgb(0,0,0);
    opacity: 0;
    transition: opacity 0.4s 0.1s,
                width 0s 0.5s,
                height 0s 0.5s;
}
.menu:focus .menu-ul {
    left: 0;
    transition: left 0.5s;
}
.menu-ul:focus  {
    left: -80vw;
}
.menu:focus .menu-ul::after{
    width: 180vw;
    height: 100vh;
    opacity: 0.4;
    transition: opacity 0.5s;
}

仕組み的に nav タグのフォーカス(:focus)で表示、ul タグのフォーカスで非表示という風になっています。

これらの挙動を:hover の動き(マウスや指でホバーすることで開閉させる)でも出来なくもなく、そちらならHTMLを本当に何もさわらずいじらずに CSS だけで実装できるのですが、:hoverですと思わぬ所で閉じたりして、ちょっと使いにくい。やはりクリックで明確に開閉できるほうが良いと思います。

とはいえ、:focusの仕様上どうしても開閉ボタンのようなトグルタイプのボタンは出来ません。なので「開ける」と「閉じる」の2種類の仕掛けが必要なのです。

注意点としてフォーカスの反応が早いので、アニメーションの仕組みによっては a 要素のリンクが反応しない、という場合があります。つまりリンクが機能するより早く閉じてしまう。

なので、閉じる側のアニメーションで0.1秒ほど遅延させています。アニメーションによっては必要ないですが、うまくリンクが機能しないときは参考にしてください。

でも実際は、「初回のメニュー表示時にクリックしたとき、リンク先に移動しない(2回目移行はできる)」といったふうに、うまく動かないこともあります。なので本格的にサイトに実装する場合は JavaScript を使ったほうが良いとは思います。本当の意味でHTMLを汚しませんしね。

まあ今回のは「こういうの作ってみたよ」という事で、誰かの何かの役に立てくれれば幸いだと思っています。

余談

ちなみに、このようなメニューを組み込むときは、多くはレスポンシブ対応デザインの場合が多いと思います。

レスポンシブ対応の方法は様々ありますが、1例としては次のようなCSSで対応できます。

/* 画面が大きければハンバーガーメニューをやめる */
@media (min-width: 576px) {
    .menu {
        position: static;
    }
    .menu-ul {
        position: static;
        display: flex;
        justify-content: space-around;
        width: auto;
        height: auto;
        padding: 0;
        transition: none;
    }
    .menu::before,
    .menu-ul::before {
        content: none;
    }
}