今回はハンバーガーメニューです!条件付きでCSSオンリーでの実装ができます。
ネイティブのJavaScript、jQueryでの実装方法もあわせて書いていきます。

目次

JavaScript ハンバーガーメニュー

ネイティブのJavaScriptで動かすハンバーガーメニューです。
下記の流れで動作します。

  • 開閉ボタン<div class=”header__menu-btn” id=”spMenuBtn”>をクリック。
  • 開閉ボタンをラップしている要素、<div class=”header__inner” id=”headerInner”>にactiveというclassが付与される。
  • <div class=”header__inner actve” id=”headerInner”>の配下にある<div class=”header__menu”> と 、
    <div class=”header__menu-btn” id=”spMenuBtn”>の中の<span>に当たるCSSが変化する。
    (ボタンがハンバーガーからバッテンに、非表示だったメニューが表示される。)
<div class="container">
    <header class="header">
        <div class="header__inner" id="headerInner">
            <a class="header__logo" href="">
                <img src="https://zenweb.info/wp-content/uploads/2023/05/hamburger-dummy-logo-blue.svg" alt="dummy logo">
            </a>
            <div class="header__menu">
                <ul class="menu__list">
                    <li class="menu__item">
                    <a class="menu__link" href="">会社概要</a>
                    </li>
                    <li class="menu__item">
                    <a class="menu__link" href="">お知らせ</a>
                    </li>
                    <li class="menu__item">
                    <a class="menu__link" href="">採用情報</a>
                    </li>
                </ul>
            </div>
            <div class="header__menu-btn" id="spMenuBtn">
                <span></span>
                <span></span>
                <span></span>
                <span>MENU</span>
            </div>
        </div>
    </header>
</div>
HTML
/* すべての画面幅で適用されるCSS  */
.container {
    width: 100%;
}
.header {
    width: 100%;
    box-shadow: 0px 4px 4px -1px rgba(0,0,0,0.23);
}
.header__inner {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.header__logo {
    display: block;
    font-size: 0;
}
.header__logo img {
    width: 100%;
    height: auto;
}

/* パソコンサイズ用のCSS  */
@media screen and (min-width:768px) {
    .header__inner {
        padding: 20px;
    }
    .header__logo {
        width: 160px;
        height: 19px;
        transition: opacity .2s ease;
    }
    .header__logo:hover {
        opacity: .7;
    }
    .menu__list {
        display: flex;
    }
    .menu__item:nth-child(n+2) {
        margin-left: 20px;
    }
    .menu__link {
        position: relative;
        display: block;
    }
    .menu__link::after {
        content: '';
        position: absolute;
        bottom: -5px;
        left: 0;
        width: 100%;
        height: 2px;
        background-color: #019ac6;
        transform: scaleX(0);
        transform-origin: left center;
        transition: transform .2s ease;
    }
    .menu__link:hover::after {
        transform: scaleX(1);
    }
    .header__menu-btn {
        display: none;
    }
}

/* スマートフォン、タブレットサイズ用のCSS */
@media screen and (max-width:767px) {
    .container {
        overflow: hidden;
    }
    .header {
        position: fixed;

        background: #fff;
    }
    .header__inner {
        position: relative;
        z-index: 10;
    }
    .header__logo {
        width: 125px;
        height: 15px;
        margin-left: 10px;
    }
    .header__menu {
        position: fixed;
        z-index: 5;
        top: 50px;
        width: 100%;
        height: calc(100vh - 50px);
        background: #fff;
        opacity: 0;
        visibility: hidden;
        transition: opacity .2s ease;
    }
    .header__inner.active .header__menu {
        opacity: 1;
        visibility: visible
    }
    .menu__list {
        border-top: 1px solid #d7d7d7;
    }
    .menu__item {
        border-bottom: 1px solid #d7d7d7;
    }
    .menu__link {
        display: block;
        padding: 13px 10px;
        color: #019ac6;
    }
    .header__menu-btn {
        position: relative;
        display: flex;
        justify-content: center;
        align-items: end;
        width: 50px;
        height: 50px;
        padding: 5px;
        cursor: pointer;
    }
    .header__menu-btn span:nth-child(-n+3) {
        position: absolute;
        display: block;
        width: 30px;
        height: 2px;
        background: #019ac6;
    }
    .header__menu-btn span:nth-child(1) {
        top: 10px;
        transition: all .2s ease;
    }
    .header__inner.active .header__menu-btn span:nth-child(1) {
        top: 20px;
        transform: rotate(45deg);
    }
    .header__menu-btn span:nth-child(2) {
        top: 18px;
        transition: opacity .2s ease;
    }
    .header__inner.active .header__menu-btn span:nth-child(2) {
        opacity: 0;
    }
    .header__menu-btn span:nth-child(3) {
        top: 26px;
        transition: all .2s ease;
    }
    .header__inner.active .header__menu-btn span:nth-child(3) {
        top: 20px;
        transform: rotate(-45deg);
    }
    .header__menu-btn span:nth-child(4) {
        font-size: 10px;
        color: #019ac6;
    }
}
CSS
// スマホ・タブレットサイズ時のみ表示されるメニューの開閉ボタンを変数に格納。
const spMenuBtn = document.getElementById("spMenuBtn");

// メニューや開閉ボタンをラップしている要素を変数に格納。
const headerInner = document.getElementById("headerInner");

// 開閉ボタンをクリックすると発火。
spMenuBtn.addEventListener("click", () => {
  // ラップ要素にactiveというクラスを付与する。
  headerInner.classList.toggle("active");
});
JavaScript

出力結果

jQuery ハンバーガーメニュー

jQueryで動かすハンバーガーメニューです。

やっていること自体はJavaScriptと同じです!
DOMの指定の仕方がネイティブのJavaScriptと比べて楽ですね。

<div class="container">
    <header class="header">
        <div class="header__inner" id="headerInner">
            <a class="header__logo" href="">
                <img src="https://zenweb.info/wp-content/uploads/2023/05/hamburger-dummy-logo-orange.svg" alt="dummy logo">
            </a>
            <div class="header__menu">
                <ul class="menu__list">
                    <li class="menu__item">
                    <a class="menu__link" href="">会社概要</a>
                    </li>
                    <li class="menu__item">
                    <a class="menu__link" href="">お知らせ</a>
                    </li>
                    <li class="menu__item">
                    <a class="menu__link" href="">採用情報</a>
                    </li>
                </ul>
            </div>
            <div class="header__menu-btn" id="spMenuBtn">
                <span></span>
                <span></span>
                <span></span>
                <span>MENU</span>
            </div>
        </div>
    </header>
</div>
HTML
/* すべての画面幅で適用されるCSS  */
.container {
    width: 100%;
}
.header {
    width: 100%;
    box-shadow: 0px 4px 4px -1px rgba(0,0,0,0.23);
}
.header__inner {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.header__logo {
    display: block;
    font-size: 0;
}
.header__logo img {
    width: 100%;
    height: auto;
}

/* パソコンサイズ用のCSS  */
@media screen and (min-width:768px) {
    .header__inner {
        padding: 20px;
    }
    .header__logo {
        width: 160px;
        height: 19px;
        transition: opacity .2s ease;
    }
    .header__logo:hover {
        opacity: .7;
    }
    .menu__list {
        display: flex;
    }
    .menu__item:nth-child(n+2) {
        margin-left: 20px;
    }
    .menu__link {
        position: relative;
        display: block;
    }
    .menu__link::after {
        content: '';
        position: absolute;
        bottom: -5px;
        left: 0;
        width: 100%;
        height: 2px;
        background-color: #F5675B;
        transform: scaleX(0);
        transform-origin: left center;
        transition: transform .2s ease;
    }
    .menu__link:hover::after {
        transform: scaleX(1);
    }
    .header__menu-btn {
        display: none;
    }
}

/* スマートフォン、タブレットサイズ用のCSS */
@media screen and (max-width:767px) {
    .container {
        overflow: hidden;
    }
    .header {
        position: fixed;
        background: #fff;
    }
    .header__inner {
        position: relative;
        z-index: 10;
    }
    .header__logo {
        width: 125px;
        height: 15px;
        margin-left: 10px;
    }
    .header__menu {
        position: fixed;
        z-index: 5;
        top: 50px;
        width: 100%;
        height: calc(100vh - 50px);
        background: #fff;
        opacity: 0;
        visibility: hidden;
        transition: opacity .2s ease;
    }
    .header__inner.active .header__menu {
        opacity: 1;
        visibility: visible
    }
    .menu__list {
        border-top: 1px solid #d7d7d7;
    }
    .menu__item {
        border-bottom: 1px solid #d7d7d7;
    }
    .menu__link {
        display: block;
        padding: 13px 10px;
        color: #F5675B;
    }
    .header__menu-btn {
        position: relative;
        display: flex;
        justify-content: center;
        align-items: end;
        width: 50px;
        height: 50px;
        padding: 5px;
        cursor: pointer;
    }
    .header__menu-btn span:nth-child(-n+3) {
        position: absolute;
        display: block;
        width: 30px;
        height: 2px;
        background: #F5675B;
    }
    .header__menu-btn span:nth-child(1) {
        top: 10px;
        transition: all .2s ease;
    }
    .header__inner.active .header__menu-btn span:nth-child(1) {
        top: 20px;
        transform: rotate(45deg);
    }
    .header__menu-btn span:nth-child(2) {
        top: 18px;
        transition: opacity .2s ease;
    }
    .header__inner.active .header__menu-btn span:nth-child(2) {
        opacity: 0;
    }
    .header__menu-btn span:nth-child(3) {
        top: 26px;
        transition: all .2s ease;
    }
    .header__inner.active .header__menu-btn span:nth-child(3) {
        top: 20px;
        transform: rotate(-45deg);
    }
    .header__menu-btn span:nth-child(4) {
        font-size: 10px;
        color: #F5675B;
    }
}
CSS
// スマホ・タブレットサイズ時のみ表示されるメニューの開閉ボタンを変数に格納。
const spMenuBtn = $("#spMenuBtn");

// メニューや開閉ボタンをラップしている要素を変数に格納。
const headerInner = $("#headerInner");

// 開閉ボタンをクリックすると発火。
spMenuBtn.click(function() {
  // ラップ要素にactiveというクラスを付与する。
  headerInner.toggleClass("active");
});
JavaScript

出力結果

CSSオンリー ハンバーガーメニュー

HTMLとCSSのみで動かすハンバーガーメニューです。
開閉ボタン部分をinputタグ(checkbox)とlabelタグにすることで、jQueryやJavaScriptを使わず実装することができます。

チェックボックスにチェックが入っているかどうかを見て、見た目を変えたい要素に当たるCSSを変更します。

チェックボックスの後の要素に当たるスタイルを変えたい場合は隣接兄弟結合子を使いましたが、今回はチェックボックスの前の要素のスタイルも変えたかったので、擬似クラス:has() を使って要素の指定を行いました。

MDN Web Docs
:has() – CSS: カスケーディングスタイルシート | MDN :has() は CSS の疑似クラス関数で、引数として渡される相対セレクターのいずれかが、その要素から辿ってアンカーとして少なくとも一つの要素とマッチする場合に、その要素…

問題点として、上記のMDNの記事にもある通り:has() は2023年6月1日現在 FireFoxで「User must explicitly enable this feature.(ユーザーはこの機能を明示的に有効にする必要があります。)」となっています。

どうやら、FireFoxで:has() を動作させるためには、ユーザー側でFireFoxブラウザの設定を変更する必要があるようです。
案件などで用いられる際はこの点にご注意ください。

下記の流れで動作します。

  • 開閉ボタンのラベル<label class=”header__menu-label” for=”spMenuBtn”>をクリック。
  • 連動するチェックボックス<input type=”checkbox” class=”header__menu-input” id=”spMenuBtn”>が、チェックが入った状態になる。
  • <input type=”checkbox” class=”header__menu-input” id=”spMenuBtn”>の直前にある<div class=”header__menu”> と 、
    直後にある<label class=”header__menu-label” for=”spMenuBtn”>の中の<span>に当たるCSSが変化する。
    (ボタンがハンバーガーからバッテンに、非表示だったメニューが表示される。)
<div class="container">
    <header class="header">
        <div class="header__inner" id="headerInner">
            <a class="header__logo" href="">
                <img src="https://zenweb.info/wp-content/uploads/2023/05/hamburger-dummy-logo-green.svg" alt="dummy logo">
            </a>
            <div class="header__menu">
                <ul class="menu__list">
                    <li class="menu__item">
                    <a class="menu__link" href="">会社概要</a>
                    </li>
                    <li class="menu__item">
                    <a class="menu__link" href="">お知らせ</a>
                    </li>
                    <li class="menu__item">
                    <a class="menu__link" href="">採用情報</a>
                    </li>
                </ul>
            </div>
            <input type="checkbox" class="header__menu-input" id="spMenuBtn">
            <label class="header__menu-label" for="spMenuBtn">
                <span></span>
                <span></span>
                <span></span>
                <span>MENU</span>
            </label>
        </div>
    </header>
</div>
HTML
/* すべての画面幅で適用されるCSS  */
.container {
    width: 100%;
}
.header {
    width: 100%;
    box-shadow: 0px 4px 4px -1px rgba(0,0,0,0.23);
}
.header__inner {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.header__logo {
    display: block;
    font-size: 0;
}
.header__logo img {
    width: 100%;
    height: auto;
}
.header__menu-input {
    display: none;
}

/* パソコンサイズ用のCSS  */
@media screen and (min-width:768px) {
    .header__inner {
        padding: 20px;
    }
    .header__logo {
        width: 160px;
        height: 19px;
        transition: opacity .2s ease;
    }
    .header__logo:hover {
        opacity: .7;
    }
    .menu__list {
        display: flex;
    }
    .menu__item:nth-child(n+2) {
        margin-left: 20px;
    }
    .menu__link {
        position: relative;
        display: block;
    }
    .menu__link::after {
        content: '';
        position: absolute;
        bottom: -5px;
        left: 0;
        width: 100%;
        height: 2px;
        background-color: #12DE40;
        transform: scaleX(0);
        transform-origin: left center;
        transition: transform .2s ease;
    }
    .menu__link:hover::after {
        transform: scaleX(1);
    }
    .header__menu-label {
        display: none;
    }
}

/* スマートフォン、タブレットサイズ用のCSS*/
@media screen and (max-width:767px) {
    .container {
        overflow: hidden;
    }
    .header {
        position: fixed;
        background: #fff;
    }
    .header__inner {
        position: relative;
        z-index: 10;
    }
    .header__logo {
        width: 125px;
        height: 15px;
        margin-left: 10px;
    }
    .header__menu {
        position: fixed;
        z-index: 5;
        top: 50px;
        width: 100%;
        height: calc(100vh - 50px);
        background: #fff;
        opacity: 0;
        visibility: hidden;
        transition: opacity .2s ease;
    }
    /*
    疑似クラス:hasを使うことで、
    直前に「.header__menu-input:checked」がある
    「.header__menu」に適用される。
    */
    .header__menu:has(+ .header__menu-input:checked ) {
        opacity: 1;
        visibility: visible
    }
    .menu__list {
        border-top: 1px solid #d7d7d7;
    }
    .menu__item {
        border-bottom: 1px solid #d7d7d7;
    }
    .menu__link {
        display: block;
        padding: 13px 10px;
        color: #12DE40;
    }
    .header__menu-label {
        position: relative;
        display: flex;
        justify-content: center;
        align-items: end;
        width: 50px;
        height: 50px;
        padding: 5px;
        cursor: pointer;
    }
    .header__menu-label span:nth-child(-n+3) {
        position: absolute;
        display: block;
        width: 30px;
        height: 2px;
        background: #12DE40;
    }
    .header__menu-label span:nth-child(1) {
        top: 10px;
        transition: all .2s ease;
    }
    .header__menu-input:checked + .header__menu-label span:nth-child(1) {
        top: 20px;
        transform: rotate(45deg);
    }
    .header__menu-label span:nth-child(2) {
        top: 18px;
        transition: opacity .2s ease;
    }
    .header__menu-input:checked + .header__menu-label span:nth-child(2) {
        opacity: 0;
    }
    .header__menu-label span:nth-child(3) {
        top: 26px;
        transition: all .2s ease;
    }
    .header__menu-input:checked + .header__menu-label span:nth-child(3) {
        top: 20px;
        transform: rotate(-45deg);
    }
    .header__menu-label span:nth-child(4) {
        font-size: 10px;
        color: #12DE40;
    }
}
CSS

出力結果

TOP