PageSourceSearch

https://www.tokyodome-hotels.co.jp/assets/hotel/scripts/header/header-sp.js

js tokyodome-hotels.co.jp collected 2026-09-24 10:46:42 UTC 11,073 bytes, 263 lines download raw bytes

1export class HeaderSP {
2    // #activeClass = 'is-active';
3    #hiddenClass = 'is-hidden';
4    #openClass = 'is-open';
5    #scrollClass = 'is-scroll';
6    #scrollLockClass = 'is-scrollLock';
7    #bodyElement;
8    // #mainElement: HTMLElement | null;
9    #headerParent;
10    #layer2ndBtns;
11    #layer2ndBtnArray;
12    #layer3rdBtns;
13    #subMenus;
14    #subMenuArray;
15    #menu4th;
16    #menu4thArray;
17    #headerMain;
18    #hamburgerBtn;
19    #hamburgerBtnText;
20    #backBtns2nd;
21    #backBtns3rd;
22    #headerPrimary;
23    #headerSecondary;
24    #menu2nd;
25    #menu2ndArray;
26    #menu3rd;
27    #menu3rdArray;
28    #menu2ndBtns;
29    #headerFocusableElements;
30    #headerFocusableElementArray;
31    // #mainFirstFocusableElement: HTMLElement | null | undefined;
32    #headerLastFocusableEl;
33    constructor(root) {
34        this.#bodyElement = document.querySelector('body');
35        // this.#mainElement = document.querySelector('main');
36        this.#headerParent = root.querySelector('.fn-header');
37        this.#headerMain = root.querySelector('.fn-header-main');
38        this.#headerPrimary = root.querySelector('.fn-header-primary');
39        this.#headerSecondary = root.querySelector('.fn-header-secondary');
40        this.#layer2ndBtns = root.querySelectorAll('.fn-btn-2ndLayer');
41        this.#layer2ndBtnArray = [...this.#layer2ndBtns];
42        this.#layer3rdBtns = root.querySelectorAll('.fn-btn-3rdLayer');
43        this.#subMenus = root.querySelectorAll('.fn-submenu');
44        this.#subMenuArray = [...this.#subMenus];
45        this.#menu4th = root.querySelectorAll('.fn-4th-menu');
46        this.#menu4thArray = [...this.#menu4th];
47        this.#hamburgerBtn = root.querySelector('.fn-hamburger-btn');
48        this.#hamburgerBtnText = root.querySelector('.fn-hamburger-text');
49        this.#backBtns2nd = root.querySelectorAll('.fn-btn-back2nd');
50        this.#backBtns3rd = root.querySelectorAll('.fn-btn-back3rd');
51        // get height
52        this.#menu2nd = root.querySelectorAll('.fn-2nd-menu');
53        // miteigi
54        this.#menu2ndArray = [...this.#menu2nd];
55        this.#menu3rd = root.querySelectorAll('.fn-3rd-menu');
56        this.#menu3rdArray = [...this.#menu3rd];
57        this.#menu2ndBtns = root.querySelectorAll('.fn-header-primary-btn');
58        // this.#focusableElement = root.querySelectorAll('a:link, button');
59        this.#headerFocusableElements =
60            this.#headerParent?.querySelectorAll('a:link, button');
61        this.#headerFocusableElementArray = [];
62        if (this.#headerFocusableElements) {
63            this.#headerFocusableElementArray = [...this.#headerFocusableElements];
64            this.#headerLastFocusableEl = this.#headerFocusableElementArray.at(-1);
65        }
66        // 実行
67        this.#setClassByScrollY();
68        this.#setMenusHidden(this.#subMenus);
69        this.#setMenusHidden(this.#menu4th);
70        this.#bindHeaderEventsSP();
71    }
72    // SP時ヘッダー処理まとめ
73    #bindHeaderEventsSP() {
74        // ハンバーガーボタン
75        if (this.#hamburgerBtn === null) {
76            return;
77        }
78        this.#hamburgerBtn.setAttribute('aria-expanded', 'false');
79        this.#hamburgerBtn.addEventListener('click', () => {
80            const isMenuOpen = this.#hamburgerBtn?.classList.contains(this.#openClass);
81            isMenuOpen ? this.#close() : this.#open();
82        });
83        // ここまで:ハンバーガーボタン
84        // 第2階層ボタン
85        this.#layer2ndBtns.forEach((btn, i) => {
86            btn.addEventListener('click', () => {
87                this.#onClickLayer2ndBtn(i);
88            });
89            // 戻るボタンクリック → 第2階層に戻る・フォーカス戻す
90            this.#backBtns2nd[i]?.addEventListener('click', () => {
91                this.#subMenuArray[i]?.classList.add(this.#hiddenClass);
92                // 第2階層ボタンをすべて表示
93                this.#menu2ndBtns.forEach((btn) => {
94                    btn.classList.remove(this.#hiddenClass);
95                });
96                btn.focus();
97            });
98            this.#setFocusOnBackBtn(this.#backBtns2nd, i);
99        });
100        // メニューが開いている時、最後のリンク上でタブキークリックでハンバーガーにフォーカスされる
101        this.#headerLastFocusableEl?.addEventListener('keydown', (e) => {
102            const isMenuOpen = this.#hamburgerBtn?.classList.contains(this.#openClass);
103            if (isMenuOpen) {
104                if (e.key === 'Tab' && !e.shiftKey) {
105                    e.preventDefault();
106                    this.#hamburgerBtn?.focus();
107                }
108            }
109        });
110        const menuLinkArray2 = document.querySelectorAll('a:link, button');
111        this.#layer3rdBtns.forEach((btn, i) => {
112            btn.addEventListener('click', () => {
113                this.#onClickLayer3rdBtn(i);
114            });
115            // 戻るボタンクリック → 第3階層に戻る・フォーカス戻す
116            this.#backBtns3rd[i]?.addEventListener('click', () => {
117                this.#menu4thArray[i]?.classList.add(this.#hiddenClass);
118                this.#backBtns2nd.forEach((btn) => {
119                    btn.classList.remove(this.#hiddenClass);
120                });
121                menuLinkArray2.forEach((el) => {
122                    el.classList.remove(this.#hiddenClass);
123                });
124                btn.focus();
125            });
126            this.#setFocusOnBackBtn(this.#backBtns3rd, i);
127        });
128        // 高さ取得とスタイル設定
129        if (this.#headerSecondary === null || this.#headerParent === null) {
130            return;
131        }
132        const secondaryMenuHeight = this.#headerSecondary.clientHeight;
133        const headerHeight = this.#headerParent.clientHeight;
134        this.#headerPrimary?.style.setProperty('--secondary-menu-height', `${secondaryMenuHeight}px`);
135        this.#headerPrimary?.style.setProperty('--header-height', `${headerHeight}px`);
136        this.#menu3rdArray.forEach((el) => {
137            el.style.setProperty('--secondary-menu-height', `${secondaryMenuHeight}px`);
138            el.style.setProperty('--header-height', `${headerHeight}px`);
139        });
140    }
141    /**
142     * 戻るボタン上でタブキー → ハンバーガーメニューにフォーカス移動
143     * @param btns 戻るボタンの配列
144     * @param i 対象の戻るボタンのindex
145     */
146    #setFocusOnBackBtn(btns, i) {
147        btns[i]?.addEventListener('keydown', (e) => {
148            if (e.key === 'Tab' && e.shiftKey) {
149                e.preventDefault();
150                this.#hamburgerBtn?.focus();
151            }
152        });
153    }
154    /**
155     * 下層メニューの表示
156     * @param layers 下層メニューリスト
157     */
158    #setMenusHidden(layers) {
159        layers.forEach((layer) => {
160            layer.classList.add(this.#hiddenClass);
161        });
162    }
163    /**
164     * 第2階層ボタンのクリックイベント
165     * @param i 選択されたボタンのindex
166     */
167    #onClickLayer2ndBtn(i) {
168        this.#subMenuArray[i]?.classList.remove(this.#hiddenClass);
169        this.#menu2ndBtns.forEach((btn) => {
170            btn.classList.add(this.#hiddenClass);
171        });
172        this.#backBtns2nd[i]?.focus();
173        const menu3rdFocusableElementArray = [
174            ...(this.#menu3rdArray[i]?.querySelectorAll('.fn-3rd-link') ?? []),
175        ];
176        const menu3rdLastFocusableEl = menu3rdFocusableElementArray.at(-1);
177        menu3rdLastFocusableEl?.addEventListener('keydown', (e) => {
178            if (e.key === 'Tab' && !e.shiftKey) {
179                e.preventDefault();
180                this.#backBtns2nd[i]?.focus();
181            }
182        });
183        // height
184        const imageHeight = this.#menu2ndArray[i]?.clientHeight;
185        this.#menu3rdArray[i]?.style.setProperty('--2ndLayer-image-height', `${imageHeight}px`);
186    }
187    // 第3階層ボタンのクリックイベント
188    #onClickLayer3rdBtn(i) {
189        this.#backBtns3rd[i]?.classList.remove(this.#hiddenClass);
190        this.#menu4thArray[i]?.classList.remove(this.#hiddenClass);
191        this.#backBtns2nd.forEach((btn) => {
192            btn.classList.add(this.#hiddenClass);
193        });
194        const menuLinkArray2 = document.querySelectorAll('.fn-menu-link');
195        menuLinkArray2.forEach((el) => {
196            el.classList.add(this.#hiddenClass);
197        });
198        this.#backBtns3rd[i]?.focus();
199        // 最後のメニュー上でtabキー → 戻るボタンにフォーカス
200        const menu4thFocusableElementArray = [
201            ...(this.#menu4thArray[i]?.querySelectorAll('a:link, button') ?? []),
202        ];
203        const menu4thLastFocusableEl = menu4thFocusableElementArray.at(-1);
204        menu4thLastFocusableEl?.addEventListener('keydown', (e) => {
205            if (e.key === 'Tab' && !e.shiftKey) {
206                e.preventDefault();
207                this.#backBtns3rd[i]?.focus();
208            }
209        });
210    }
211    /**
212     * ハンバーガーメニュー閉じる
213     */
214    #close() {
215        this.#bodyElement?.classList.remove(this.#scrollLockClass);
216        this.#headerParent?.classList.remove(this.#openClass);
217        this.#headerMain?.classList.remove(this.#openClass);
218        this.#hamburgerBtn?.classList.remove(this.#openClass);
219        this.#hamburgerBtn?.setAttribute('aria-expanded', 'false');
220        if (this.#hamburgerBtnText) {
221            this.#hamburgerBtnText.textContent = 'メニューを開く';
222        }
223    }
224    /**
225     * ハンバーガーメニュー開く
226     */
227    #open() {
228        this.#bodyElement?.classList.add(this.#scrollLockClass);
229        this.#headerParent?.classList.add(this.#openClass);
230        this.#headerMain?.classList.add(this.#openClass);
231        this.#hamburgerBtn?.classList.add(this.#openClass);
232        this.#hamburgerBtn?.setAttribute('aria-expanded', 'true');
233        if (this.#hamburgerBtnText) {
234            this.#hamburgerBtnText.textContent = 'メニューを閉じる';
235        }
236        this.#hamburgerBtn?.blur();
237        this.#layer2ndBtnArray[0]?.focus();
238    }
239    /**
240     * 画面スクロールによるクラス付与(トップのみ)
241     */
242    #setClassByScrollY() {
243        let lastScrollY = window.scrollY;
244        window.addEventListener('scroll', () => {
245            const currentY = window.scrollY;
246            if (this.#headerParent) {
247                this.#headerParent.classList.toggle(this.#scrollClass, window.scrollY > 72);
248                // ヘッダー分スクロール以下で機能させる
249                if (currentY >= 72) {
250                    if (currentY > lastScrollY) {
251                        // 下にスクロール → 非表示
252                        this.#headerParent.classList.add('is-none');
253                    }
254                    else {
255                        // 上にスクロール → 表示
256                        this.#headerParent.classList.remove('is-none');
257                    }
258                }
259            }
260            lastScrollY = currentY;
261        });
262    }
263}

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.