PageSourceSearch

https://www.ridgelinez.com/core/wp-content/themes/ridgelinez/_assets/js/module/gNav.js

js ridgelinez.com collected 2026-10-02 02:18:32 UTC 7,576 bytes, 236 lines download raw bytes

1export const gNav = () => {
2  const navButtons = document.querySelectorAll("[data-nav-target]");
3  const navBodies = document.querySelectorAll("[data-nav]");
4  const navTitles = document.querySelectorAll(
5    ".globalHeaderNav-list-item-title"
6  );
7  const navLinks = document.querySelectorAll(".globalHeaderNav-list-item-link");
8  const navAreas = document.querySelectorAll(".globalHeaderSubNav");
9  const header = document.querySelector(".globalHeader");
10  const hamburgerButton = document.querySelector(".globalHeaderNav-button");
11  const body = document.body;
12  let timeoutId;
13  let isMouseOverNav = false;
14
15  const mediaQuery = window.matchMedia("(max-width: 1024px)");
16
17  // ナビゲーションを閉じる関数を分割
18  const closeNavAreas = () => {
19    navAreas.forEach((area) => area.classList.remove("is-active"));
20  };
21
22  const closeAllNavs = () => {
23    navButtons.forEach((btn) => btn.classList.remove("is-active"));
24    navBodies.forEach((body) => body.classList.remove("is-active"));
25    closeNavAreas();
26  };
27
28  // 初期状態で最初のナビをアクティブにする
29  const initializeFirstNav = () => {
30    if (navButtons.length > 0 && navBodies.length > 0) {
31      const firstButton = navButtons[0];
32      const firstTarget = firstButton.getAttribute("data-nav-target");
33      const firstBody = document.querySelector(`[data-nav="${firstTarget}"]`);
34
35      firstButton.classList.add("is-active");
36      if (firstBody) {
37        firstBody.classList.add("is-active");
38      }
39    }
40  };
41
42  // ハンバーガーメニューの処理
43  const handleHamburgerClick = () => {
44    const isOpen = body.classList.contains("nav-open");
45
46    if (isOpen) {
47      // メニューを閉じる
48      body.classList.remove("nav-open");
49      hamburgerButton.classList.remove("is-active");
50      hamburgerButton.setAttribute("aria-label", "ナビゲーションを開く");
51      // サブナビのみを閉じる
52      closeNavAreas();
53    } else {
54      // メニューを開く
55      body.classList.add("nav-open");
56      hamburgerButton.classList.add("is-active");
57      hamburgerButton.setAttribute("aria-label", "ナビゲーションを閉じる");
58    }
59  };
60
61  // ナビゲーションの処理
62  const handleNavInteraction = (e, isMobile) => {
63    const button = e.currentTarget;
64    const target = button.getAttribute("data-nav-target");
65
66    if (isMobile) {
67      // スマホ表示時はトグル動作
68      if (button.classList.contains("is-active")) {
69        // アクティブな場合は閉じる
70        button.classList.remove("is-active");
71        document
72          .querySelector(`[data-nav="${target}"]`)
73          ?.classList.remove("is-active");
74      } else {
75        // 他のメニューを閉じて、クリックされたメニューを開く
76        navButtons.forEach((btn) => btn.classList.remove("is-active"));
77        navBodies.forEach((body) => body.classList.remove("is-active"));
78        button.classList.add("is-active");
79        document
80          .querySelector(`[data-nav="${target}"]`)
81          ?.classList.add("is-active");
82      }
83    } else {
84      // PC表示時は既存の動作
85      navButtons.forEach((btn) => btn.classList.remove("is-active"));
86      navBodies.forEach((body) => body.classList.remove("is-active"));
87      button.classList.add("is-active");
88      document
89        .querySelector(`[data-nav="${target}"]`)
90        ?.classList.add("is-active");
91    }
92  };
93
94  // タイトルの処理
95  const handleTitleInteraction = (e, isMobile) => {
96    const title = e.currentTarget;
97    const subNav = title.nextElementSibling;
98
99    if (isMobile) {
100      // スマホ表示時はトグル動作
101      if (subNav?.classList.contains("is-active")) {
102        subNav.classList.remove("is-active");
103      } else {
104        document
105          .querySelectorAll(".globalHeaderSubNav")
106          .forEach((nav) => nav.classList.remove("is-active"));
107        subNav?.classList.add("is-active");
108      }
109    } else {
110      // PC表示時は既存の動作
111      document
112        .querySelectorAll(".globalHeaderSubNav")
113        .forEach((nav) => nav.classList.remove("is-active"));
114      subNav?.classList.add("is-active");
115    }
116  };
117
118  // マウスエンター処理
119  const handleMouseEnter = () => {
120    isMouseOverNav = true;
121    if (timeoutId) {
122      clearTimeout(timeoutId);
123      timeoutId = null;
124    }
125  };
126
127  // マウスリーブ処理
128  const handleMouseLeave = () => {
129    isMouseOverNav = false;
130    if (!mediaQuery.matches) {
131      timeoutId = setTimeout(() => {
132        if (!isMouseOverNav) {
133          // サブナビのみを閉じる
134          closeNavAreas();
135        }
136      }, 300);
137    }
138  };
139
140  // navLinksのイベントハンドラー
141  const handleLinkInteraction = () => {
142    closeNavAreas();
143  };
144
145  // イベントハンドラーの設定
146  const setEventHandlers = (isMobile) => {
147    const eventType = isMobile ? "click" : "mouseenter";
148
149    // 既存のイベントリスナーをすべて削除
150    const removeAllListeners = () => {
151      [header, ...navAreas, ...navBodies].forEach((element) => {
152        if (element) {
153          element.removeEventListener("mouseenter", handleMouseEnter);
154          element.removeEventListener("mouseleave", handleMouseLeave);
155        }
156      });
157    };
158
159    removeAllListeners();
160
161    if (!isMobile) {
162      // PC表示時のマウスイベント設定
163      if (header) {
164        header.addEventListener("mouseleave", handleMouseLeave);
165      }
166
167      // サブナビエリアのイベント
168      navAreas.forEach((area) => {
169        area.addEventListener("mouseenter", handleMouseEnter);
170      });
171
172      // ナビボディのイベント
173      navBodies.forEach((body) => {
174        body.addEventListener("mouseenter", handleMouseEnter);
175      });
176    }
177
178    navButtons.forEach((button) => {
179      button.removeEventListener("mouseenter", (e) =>
180        handleNavInteraction(e, false)
181      );
182      button.removeEventListener("click", (e) => handleNavInteraction(e, true));
183      button.addEventListener(eventType, (e) =>
184        handleNavInteraction(e, isMobile)
185      );
186    });
187
188    navTitles.forEach((title) => {
189      title.removeEventListener("mouseenter", (e) =>
190        handleTitleInteraction(e, false)
191      );
192      title.removeEventListener("click", (e) =>
193        handleTitleInteraction(e, true)
194      );
195      title.addEventListener(eventType, (e) =>
196        handleTitleInteraction(e, isMobile)
197      );
198    });
199
200    navLinks.forEach((link) => {
201      link.removeEventListener("mouseenter", handleLinkInteraction);
202      link.removeEventListener("click", handleLinkInteraction);
203      if (!isMobile) {
204        link.addEventListener("mouseenter", handleLinkInteraction);
205      }
206    });
207
208    // ハンバーガーメニューのイベント設定
209    if (hamburgerButton) {
210      hamburgerButton.removeEventListener("click", handleHamburgerClick);
211      if (isMobile) {
212        hamburgerButton.addEventListener("click", handleHamburgerClick);
213      } else {
214        // PC表示時はメニューを閉じる
215        body.classList.remove("nav-open");
216        hamburgerButton.classList.remove("is-active");
217        hamburgerButton.setAttribute("aria-label", "ナビゲーションを開く");
218      }
219    }
220  };
221
222  // 初期設定
223  setEventHandlers(mediaQuery.matches);
224  if (!mediaQuery.matches) {
225    initializeFirstNav();
226  }
227
228  // ブレイクポイントの変更を監視
229  mediaQuery.addEventListener("change", (e) => {
230    setEventHandlers(e.matches);
231    if (!e.matches) {
232      initializeFirstNav();
233    }
234  });
235};
236

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.