PageSourceSearch

https://next-level.biz/wp-content/themes/next-level/js/main.js

js next-level.biz collected 2026-09-28 10:21:58 UTC 9,042 bytes, 313 lines download raw bytes

1/**
2 * Main.js - Vanilla JS版
3 * jQueryからVanilla JSに変換
4 */
5
6(function() {
7	"use strict";
8	
9	// DOM読み込み完了時に実行
10	document.addEventListener('DOMContentLoaded', function() {
11		
12		// ヘルパー関数
13		function getWindowWidth() {
14			return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
15		}
16
17		const slideTimers = new WeakMap();
18
19		function clearSlideTimer(element) {
20			const timer = slideTimers.get(element);
21			if (timer) {
22				clearTimeout(timer);
23				slideTimers.delete(element);
24			}
25		}
26		
27		function slideToggle(element, duration = 400) {
28			if (element.dataset.slideState === 'closing' || window.getComputedStyle(element).display === 'none') {
29				slideDown(element, duration);
30			} else {
31				slideUp(element, duration);
32			}
33		}
34		
35		function slideDown(element, duration = 400) {
36			clearSlideTimer(element);
37
38			const isHidden = window.getComputedStyle(element).display === 'none';
39			const currentHeight = isHidden ? 0 : element.getBoundingClientRect().height;
40
41			if (isHidden) {
42				element.style.display = 'block';
43			}
44
45			element.style.transition = 'none';
46			element.style.height = currentHeight + 'px';
47			element.style.overflow = 'hidden';
48			const height = element.scrollHeight;
49			element.offsetHeight;
50
51			element.dataset.slideState = 'opening';
52			element.style.transition = `height ${duration}ms ease`;
53			element.style.height = height + 'px';
54			
55			const timer = setTimeout(() => {
56				element.style.height = '';
57				element.style.overflow = '';
58				element.style.transition = '';
59				delete element.dataset.slideState;
60				slideTimers.delete(element);
61			}, duration);
62			slideTimers.set(element, timer);
63		}
64		
65		function slideUp(element, duration = 400) {
66			if (window.getComputedStyle(element).display === 'none') {
67				return;
68			}
69
70			clearSlideTimer(element);
71
72			const currentHeight = element.getBoundingClientRect().height;
73			element.style.transition = 'none';
74			element.style.height = currentHeight + 'px';
75			element.style.overflow = 'hidden';
76			element.offsetHeight;
77
78			element.dataset.slideState = 'closing';
79			element.style.transition = `height ${duration}ms ease`;
80			element.style.height = '0';
81
82			const timer = setTimeout(() => {
83				element.style.display = 'none';
84				element.style.height = '';
85				element.style.overflow = '';
86				element.style.transition = '';
87				delete element.dataset.slideState;
88				slideTimers.delete(element);
89			}, duration);
90			slideTimers.set(element, timer);
91		}
92		
93		function smoothScrollTo(targetY, duration = 700) {
94			const startY = window.pageYOffset;
95			const diff = targetY - startY;
96			const startTime = performance.now();
97			
98			function step(currentTime) {
99				const elapsed = currentTime - startTime;
100				const progress = Math.min(elapsed / duration, 1);
101				
102				// イージング関数 (ease-out)
103				const eased = 1 - Math.pow(1 - progress, 3);
104				
105				window.scrollTo(0, startY + diff * eased);
106				
107				if (progress < 1) {
108					requestAnimationFrame(step);
109				}
110			}
111			
112			requestAnimationFrame(step);
113		}
114
115		/* Header Show Submenu */
116		if (getWindowWidth() >= 768) {
117			// ヘッダーメニューのホバー処理
118			const headerMenuItems = document.querySelectorAll('.header-menu .nav-top ul li');
119			headerMenuItems.forEach(item => {
120				item.addEventListener('mouseenter', function() {
121					const submenu = this.querySelector('ul');
122					if (submenu) {
123						slideDown(submenu, 200);
124					}
125				});
126				
127				item.addEventListener('mouseleave', function() {
128					const submenu = this.querySelector('ul');
129					if (submenu) {
130						slideUp(submenu, 200);
131					}
132				});
133			});
134			
135			// フッターメニューのホバー処理
136			const footerMenuItems = document.querySelectorAll('.small-nav-footer .nav-footer ul li');
137			footerMenuItems.forEach(item => {
138				item.addEventListener('mouseenter', function() {
139					const submenu = this.querySelector('ul');
140					if (submenu) {
141						slideDown(submenu, 400);
142					}
143				});
144				
145				item.addEventListener('mouseleave', function() {
146					const submenu = this.querySelector('ul');
147					if (submenu) {
148						slideUp(submenu, 400);
149					}
150				});
151			});
152		}
153		/* Header Show Submenu */
154
155		/* Footer Show Submenu */
156		if (getWindowWidth() <= 767) {
157			// モバイル用ヘッダーメニューのクリック処理
158			const mobileHeaderMenuItems = document.querySelectorAll('.header-menu .nav-top ul li');
159			mobileHeaderMenuItems.forEach(item => {
160				const directLink = Array.from(item.children).find(child => child.tagName === 'A');
161				const submenu = Array.from(item.children).find(child => child.tagName === 'UL');
162
163				if (directLink) {
164					const linkPath = new URL(directLink.href, window.location.origin).pathname.replace(/\/+$/, '');
165					if (linkPath.endsWith('/office')) {
166						if (submenu) {
167							submenu.style.display = 'none';
168							submenu.setAttribute('aria-hidden', 'true');
169						}
170						return;
171					}
172				}
173
174				item.addEventListener('click', function() {
175					const childMenu = this.querySelector('ul');
176					if (childMenu) {
177						slideToggle(childMenu, 400);
178					}
179				});
180			});
181
182			// フッターメニューのクリック処理(最初以外)
183			const footerMenuItemsNotFirst = document.querySelectorAll('.small-nav-footer .nav-footer ul li:not(:first-child)');
184			footerMenuItemsNotFirst.forEach(item => {
185				item.addEventListener('click', function() {
186					const showMenu = document.querySelector('.show-menu');
187					
188					if (showMenu) {
189						// .show-menuが非表示の場合、表示
190						if (window.getComputedStyle(showMenu).display === 'none') {
191							slideToggle(showMenu, 400);
192						}
193
194						// 既存のulを削除
195						const existingUl = showMenu.querySelector('ul');
196						if (existingUl) {
197							existingUl.remove();
198						}
199
200						// 現在のアイテムのulをクローン
201						const submenu = this.querySelector('ul');
202						if (submenu) {
203							const clonedSubmenu = submenu.cloneNode(true);
204							showMenu.appendChild(clonedSubmenu);
205						}
206
207						// アクティブクラスの処理
208						if (this.classList.contains('active')) {
209							footerMenuItemsNotFirst.forEach(otherItem => {
210								otherItem.classList.remove('active');
211							});
212							slideToggle(showMenu, 400);
213						} else {
214							footerMenuItemsNotFirst.forEach(otherItem => {
215								otherItem.classList.remove('active');
216							});
217							this.classList.add('active');
218						}
219					}
220				});
221			});
222
223			// フッターメニューの最初のアイテムのクリック処理
224			const firstFooterMenuItem = document.querySelector('.small-nav-footer .nav-footer ul li:first-child');
225			if (firstFooterMenuItem) {
226				firstFooterMenuItem.addEventListener('click', function() {
227					const submenu = this.querySelector('ul');
228					if (submenu) {
229						slideToggle(submenu, 400);
230					}
231				});
232			}
233		}
234		/* End Footer Show Submenu */
235
236		// リクルートボックスのスムーススクロール
237		const recruitLinks = document.querySelectorAll(".box-recruite a");
238		recruitLinks.forEach(link => {
239			link.addEventListener('click', function(e) {
240				e.preventDefault();
241				const href = this.getAttribute("href");
242				if (href && href.startsWith('#')) {
243					const target = document.querySelector(href);
244					if (target) {
245						const targetY = target.offsetTop;
246						smoothScrollTo(targetY, 700);
247					}
248				}
249			});
250		});
251
252		// ウィジェットULのクリック処理
253		const widgetItems = document.querySelectorAll('.widgetUL li');
254		widgetItems.forEach(item => {
255			item.addEventListener('click', function(e) {
256				e.preventDefault();
257				const smallUL = this.querySelector('.smallUL');
258				if (smallUL) {
259					this.classList.toggle('active');
260					slideToggle(smallUL, 400);
261				}
262				return false;
263			});
264		});
265
266		// メニューソーシャルULの最後の子のクリック処理
267		const lastSocialItem = document.querySelector('.menu-small .socialUL li:last-child');
268		if (lastSocialItem) {
269			lastSocialItem.addEventListener('click', function(e) {
270				e.preventDefault();
271				const submenu = this.querySelector('ul');
272				if (submenu) {
273					slideToggle(submenu, 400);
274				}
275				return false;
276			});
277		}
278	});
279
280	/* Page Top */
281	const scrollToTopBtn = document.querySelector('.scroll_to_top');
282	if (scrollToTopBtn) {
283		scrollToTopBtn.addEventListener('click', function(e) {
284			e.preventDefault();
285			smoothScrollTo(0, 700);
286		});
287	}
288	/* End Page Top */
289
290	// スムーススクロール関数を再利用可能にする
291	function smoothScrollTo(targetY, duration = 700) {
292		const startY = window.pageYOffset;
293		const diff = targetY - startY;
294		const startTime = performance.now();
295		
296		function step(currentTime) {
297			const elapsed = currentTime - startTime;
298			const progress = Math.min(elapsed / duration, 1);
299			
300			// イージング関数 (ease-out)
301			const eased = 1 - Math.pow(1 - progress, 3);
302			
303			window.scrollTo(0, startY + diff * eased);
304			
305			if (progress < 1) {
306				requestAnimationFrame(step);
307			}
308		}
309		
310		requestAnimationFrame(step);
311	}
312
313})();

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.