JavaScriptでナビゲーション以外の場所をクリックしたらナビゲーションを閉じる

JavaScriptでナビゲーション以外の場所をクリックしたらナビゲーションを閉じる

画面右上の丸いトグルボタンで、サイドナビゲーションの表示・非表示を切り替え。
ナビゲーション以外の部分をクリックすると、メニューは自動的に閉じる。
キーボードのESCキーでもメニューを閉じることができる。
classList.toggle() を使いコードを簡潔に。
contains() メソッドでクリック対象がメニュー内かを判定。

クリックでナビを閉じる機能の実装例

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="author" content="X Media" />
    <title>クリック対象外の領域をクリックした時の処理</title>
    <style>
        html {
            --font-color: #7aabd6;
            --hover-color: rgba(36, 97, 151, 0.2);
            --background-color: #246197;
            font-family: 'Courier New', Courier, monospace;
            font-size: 15px;
        }

        *,
        :before,
        :after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        ::selection {
            color: var(--background-color);
            background-color: var(--font-color);
        }

        body {
            min-height: 100vh;
        }

        .toggle-btn {
            position: fixed;
            top: 10px;
            right: 10px;
            z-index: 5;
            width: 40px;
            height: 40px;
            cursor: pointer;
            transition: background-color 0.2s linear 0s;
            border-radius: 100em;
        }

        .toggle-btn:hover {
            background-color: var(--hover-color);
        }

        .toggle-btn:before,
        .toggle-btn:after {
            content: "";
            display: block;
            width: 50%;
            height: 3px;
            background-color: var(--background-color);
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -5px);
            transition: transform 0.4s cubic-bezier(.88, .22, .17, .98) 0s,
                background-color 0.4s cubic-bezier(.88, .22, .17, .98) 0s;
        }

        .toggle-btn:after {
            transform: translate(-50%, 2px);
        }

        .mobile-navigation {
            color: var(--font-color);
            width: 200px;
            height: 100%;
            position: fixed;
            top: 0;
            right: 0;
            background-color: var(--background-color);
            z-index: 4;
            padding: 100px 20px 30px;
            text-align: center;
            transform: translateX(101%);
            pointer-events: none;
            opacity: 0;
            transition: transform 0.3s cubic-bezier(.56, .15, .35, .99) 0s,
                opacity 0s linear 0.3s,
                pointer-events 0s linear 0.3s;
        }

        /* メニュー表示時のスタイル */
        .show-mobile-navigation .mobile-navigation {
            transform: translateX(0);
            pointer-events: unset;
            opacity: 1;
            transition: transform 0.5s cubic-bezier(.56, .15, .35, .99) 0s,
                opacity 0s linear 0s,
                pointer-events 0s linear 0s;
        }

        /* トグルボタンの×表示 */
        .show-mobile-navigation .toggle-btn:before,
        .show-mobile-navigation .toggle-btn:after {
            transform: translate(-50%, -50%) rotate(45deg);
            background-color: var(--font-color);
        }

        .show-mobile-navigation .toggle-btn:after {
            transform: translate(-50%, -50%) rotate(-45deg);
        }
    </style>
</head>

<body>
    <div class="toggle-btn"></div>
    <div class="mobile-navigation">ここにモバイルナビゲーションの内容が入ります。</div>

    <script>
        document.addEventListener("DOMContentLoaded", () => {
            const body = document.body;
            const toggleBtn = document.querySelector(".toggle-btn");
            const mobileNav = document.querySelector(".mobile-navigation");

            // トグルボタンがクリックされたときの処理
            toggleBtn.addEventListener("click", (event) => {
                event.stopPropagation();
                // メニューの表示・非表示を切り替える
                body.classList.toggle("show-mobile-navigation");
            });

            // ドキュメント全体がクリックされたときの処理
            document.addEventListener("click", (event) => {
                const target = event.target;
                // メニューが表示されていて、かつクリックがメニュー外かつトグルボタン外ならメニューを閉じる
                if (
                    body.classList.contains("show-mobile-navigation") &&
                    !mobileNav.contains(target) &&
                    !toggleBtn.contains(target)
                ) {
                    body.classList.remove("show-mobile-navigation");
                }
            });

            // ESCキーが押されたときの処理(メニューが表示されていれば閉じる)
            document.addEventListener("keydown", (event) => {
                if (event.key === "Escape" && body.classList.contains("show-mobile-navigation")) {
                    body.classList.remove("show-mobile-navigation");
                }
            });
        });
    </script>
</body>

</html>

コード解説

このコードは、モバイル向けのサイドナビゲーションメニューを実装しています。右上の丸いボタンをクリックすると、右側からナビゲーションがスライドして表示されます。ナビゲーション以外の領域をクリックすると自動でメニューが閉じ、ESCキーでもメニューを閉じることが可能です。

  • HTML:ボタンとナビゲーションの構造を定義。
  • CSS:ボタンやメニューのスタイル、表示・非表示時のアニメーションを制御。
  • JavaScript:ボタンのクリックでメニュー表示切替、画面外クリックでメニュー非表示、ESCキー押下でメニュー閉じる挙動を制御。

注意点・補足

  • ボタンのサイズやクリック可能範囲は適切に設定し、誤操作を防ぎましょう。
  • メニューの中身が多い場合、スクロールや表示パフォーマンスの調整が必要になることがあります。
  • classListcontains() は主要ブラウザで対応済みですが、古いブラウザでは動作確認が必要です。
  • イベントリスナーの解除処理は実装していません。動的に要素を追加・削除する場合は注意が必要です。
  • ESCキーイベントはデフォルト動作を妨げないため、特定のケースでは別のショートカットに影響する可能性があります。

Web制作現場でよくある質問(FAQ)

Q1: なぜボタンクリック時にevent.stopPropagation()を使うのですか?

A1: クリックイベントのバブリングを防ぎ、ボタンをクリックした直後にドキュメントのクリックイベントが発火してメニューがすぐ閉じるのを防止します。

Q2: contains() メソッドの役割は?

A2: クリックされた要素がメニュー内またはボタン内かどうかを判定するために使用しています。

Q3: classList.toggle()を使う利点は?

A3: メニューの表示・非表示の切替処理が簡潔になり、コードの可読性と保守性が向上します。

Q4: ESCキーで閉じる機能はなぜ重要?

A4: キーボード操作での利便性やアクセシビリティ向上のため、視覚障害者などの利用も考慮しています。


まとめ

本コードはシンプルかつ実用的なモバイル向けサイドナビゲーションの実装例です。CSSのトランジションとJavaScriptのイベント管理を組み合わせることで、スムーズで直感的な操作を実現しています。現代のDOM APIを活用し、パフォーマンスと保守性のバランスも良好です。

より高度な機能や複雑なメニュー構造が必要な場合は、この基盤に機能追加する形で拡張が可能です。

関連記事
【CSSテクニック】:not()セレクタで階層を跨いだ除外を実現
:not()疑似クラスはCSSでの除外指定に便利ですが、親要素や階層関係に対しては思わぬ制約があります。本記事では、WordPressの投稿構造を例に、インラインコードとコードブロックを正確に区別するためのセレクタ設計を解説します。
無敵の小さな蚊
無敵の小さな蚊
9ヶ月前
【CSSテクニック】:not()セレクタで階層を跨いだ除外を実現
和暦から西暦変換ツール
このツールは、日本の元号(和暦)と西暦を相互に変換できるシンプルな変換ツールです。「昭和64年は何年?」「2025年は令和何年?」といった場面で、すぐに答えがわかります。 履歴書の作成、行政手続き、年齢計算、歴史的資料の読み解きなどにご活用 […]
aki0o0
aki0o0
9ヶ月前
和暦から西暦変換ツール
JavaScriptでURL(パス)からファイル名を取得する
window.location.pathname でURLのパス部分を取得。 lastIndexOf('/') で最後のスラッシュ位置を検出。 substring でスラッシュの次から最後までを切り出し、ファイル名を抽出。
Details
Details
10ヶ月前
JavaScriptでURL(パス)からファイル名を取得する
【SEO基礎】rel=”nofollow”とは?リンクの評価をコントロールするHTML属性を解説
SEO対策に欠かせない「rel="nofollow"」の意味と使い方を解説。広告やユーザー投稿に適切なrel属性を設定し、検索エンジンとの正しい関係を築きましょう。
qqplus
qqplus
9ヶ月前
【SEO基礎】rel=”nofollow”とは?リンクの評価をコントロールするHTML属性を解説
ユーザーのブラウザに保存されているJavaScript、CSS、画像のキャッシュを削除するにはどうすればいいですか?
ブラウザのキャッシュによる古いJavaScript、CSS、画像の表示を防ぐには、URLにバージョン番号や日時パラメータを付けてリソースを更新する方法が有効です。 毎回タイムスタンプを使うとキャッシュ効果がなくなるため、手動や適切なタイミングでバージョン番号を管理するのがおすすめです。
Details
Details
10ヶ月前
ユーザーのブラウザに保存されているJavaScript、CSS、画像のキャッシュを削除するにはどうすればいいですか?
第1回:ウェブにおける色の基礎(2025年最新版)
ウェブデザインの基礎「色」について、2025年最新トレンドやアクセシビリティ・ダークモード対応まで、実務ですぐ使えるCSS色指定方法を分かりやすく解説。初心者から現場のデザイナーまで必見のカラーマネジメント入門ガイド。
aki0o0
aki0o0
10ヶ月前
iPhone(iOS Safari)における input タグの角丸問題とその対策
iPhone(iOS Safari)でinputタグに自動で適用される角丸スタイルを無効化する方法を詳しく解説。CSSによるリセットやカスタムスタイルの実装手順を、具体的なコードとともに紹介します。
キウイフルーツ
キウイフルーツ
9ヶ月前
iPhone(iOS Safari)における input タグの角丸問題とその対策
【CSS & JavaScript & SVG対応】円グラフ(扇形図)のアニメーション表現まとめ
はじめに 円グラフ(パイチャート)は、比率や構成比を視覚的に伝えるのに最適なUIコンポーネントです。最近、業務で使う機会があり、さまざまな方法でエフェクトをつけて表示する実装を試しました。 この記事では、以下の3パターンで実装する方法を紹介 […]
キウイフルーツ
キウイフルーツ
9ヶ月前
【CSS & JavaScript & SVG対応】円グラフ(扇形図)のアニメーション表現まとめ
ページ遷移の一般的な方法
本記事では、Webページにおけるページ遷移(リダイレクト)の一般的な実装方法について紹介しています。JavaScript、HTMLのメタタグ、PHP、ASPを用いたリダイレクト方法をそれぞれ具体的なコード例と共に解説しています。
Details
Details
10ヶ月前
ページ遷移の一般的な方法
scroll-behaviorで平滑スクロール、一行でいけるんだ
一般的に、アンカーリンクをスムーズにスクロールさせる時、JavaScriptやjQueryで実装することが多いです。例えば、 でも実は、CSSだけでもできるんです。そのためのプロパティがこちら: これを書くだけで、リンクをクリックした時にス […]
メモ・ノートスケ
メモ・ノートスケ
10ヶ月前
scroll-behaviorで平滑スクロール、一行でいけるんだ
CSSで等間隔配置を実現:1行に複数または複数行に配置
CSSでは、特にレスポンシブデザインにおいて、複数の要素を等間隔に配置する必要があります。列数や間隔を動的に制御することで、柔軟なレイアウトが実現可能です。この記事では、FlexboxとGridの2つの方法を使って、1行に複数のアイテムを等間隔に配置する方法を紹介します。コード内の変数(列数や間隔)を調整することで、1行に表示するアイテム数やその間隔を簡単に管理でき、レイアウト調整が容易になり、開発効率が大幅に向上します。
無敵の小さな蚊
無敵の小さな蚊
9ヶ月前
CSSで等間隔配置を実現:1行に複数または複数行に配置
日本語用WebサイトのCSSフォント設定(日本語フォント/游ゴシック/游明朝)
日本語フォントはOSにより異なるため、複数のフォントを優先順に指定する。 "Yu Gothic"や"Yu Mincho"はモダンな日本語フォントでWindows 8.1以降に搭載。 "ヒラギノ角ゴ"や"ヒラギノ明朝"はmacOS向けで、高品質。 "メイリオ"や"MS 明朝"などは古いWindows環境にも対応。 ゴシック体はカジュアル・読みやすさ重視、明朝体はフォーマル・高級感重視に適している。
Details
Details
10ヶ月前
日本語用WebサイトのCSSフォント設定(日本語フォント/游ゴシック/游明朝)
inputのプレースホルダー(placeholder)の色を変更する
プレースホルダーの色は::placeholder疑似要素で変更可能 IEやEdgeなどの古いブラウザ向けに:-ms-input-placeholder、::-ms-input-placeholderを併用 Firefoxでは透明度の調整のためopacity指定が推奨される
Details
Details
10ヶ月前
inputのプレースホルダー(placeholder)の色を変更する
IT業界の初心者が知っておくべきの用語(2025版)
新しいテクノロジーについて話すとき、時々自分がみんなのペースについていけないと感じることはありませんか?あるいは、その業界にいるのに、自分はまだ十分ではないと感じ、多くの専門用語を理解しなければならないこともあるでしょう。
Details
Details
9ヶ月前
IT業界の初心者が知っておくべきの用語(2025版)
SCSSの @for ループ(ループ文)
SCSSの @for ループ
無敵の小さな蚊
無敵の小さな蚊
10ヶ月前
SCSSの @for ループ(ループ文)