PageSourceSearch

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

js tokyodome-hotels.co.jp collected 2026-09-24 10:46:41 UTC 8,155 bytes, 218 lines download raw bytes

1export class HeaderPC {
2    #activeClass = 'is-active';
3    #hiddenClass = 'is-hidden';
4    #openClass = 'is-open';
5    #scrollClass = 'is-scroll';
6    #scrollLockClass = 'is-scrollLock';
7    #cssVariablesName = '--header-height';
8    #htmlElement;
9    #bodyElement;
10    #mainElement;
11    #headerParent;
12    #layer2ndBtns;
13    #layer3rdBtns;
14    #dropdownMenus;
15    #dropdownMenuArray;
16    #dropdownMenuCloseBtn = '.fn-dropdown-closeBtn';
17    #menu4th;
18    #menu4thArray;
19    #backdrop;
20    #menuWrap;
21    #menuWrapHeight;
22    #headerFocusableElements;
23    #headerFocusableElementArray;
24    #mainFirstFocusableElement;
25    #headerlastFocusableEl;
26    constructor(root) {
27        this.#htmlElement = document.querySelector('html');
28        this.#bodyElement = document.querySelector('body');
29        this.#mainElement = document.querySelector('main');
30        this.#headerParent = root.querySelector('.fn-header');
31        this.#menuWrap = root.querySelector('.fn-menu-wrap');
32        this.#menuWrapHeight = 0;
33        this.#layer2ndBtns = root.querySelectorAll('.fn-btn-2ndLayer');
34        this.#dropdownMenus = root.querySelectorAll('.fn-dropdown');
35        this.#dropdownMenuArray = [...this.#dropdownMenus];
36        this.#layer3rdBtns = root.querySelectorAll('.fn-btn-3rdLayer');
37        this.#menu4th = root.querySelectorAll('.fn-4th-menu');
38        this.#menu4thArray = [...this.#menu4th];
39        this.#backdrop = root.querySelector('.fn-backdrop');
40        this.#headerFocusableElements =
41            this.#headerParent?.querySelectorAll('a:link, button');
42        this.#headerFocusableElementArray = [];
43        this.#mainFirstFocusableElement =
44            this.#mainElement?.querySelector('a:link, button');
45        if (this.#headerFocusableElements) {
46            this.#headerFocusableElementArray = [...this.#headerFocusableElements];
47            this.#headerlastFocusableEl = this.#headerFocusableElementArray.at(-1);
48        }
49        // 実行
50        this.#setClassByScrollY();
51        this.#setMenusHidden(this.#dropdownMenus);
52        this.#setMenusHidden(this.#menu4th);
53        this.#bindHeaderEventsPC();
54    }
55    /** PC時ヘッダー処理まとめ */
56    #bindHeaderEventsPC() {
57        this.#updataHeaderHeight();
58        this.#layer2ndBtns.forEach((btn, i) => {
59            btn.addEventListener('click', () => {
60                this.#onClickLayer2ndBtn(btn, i);
61                if (this.#headerlastFocusableEl && this.#mainFirstFocusableElement) {
62                    this.#setFocus(this.#headerlastFocusableEl, this.#mainFirstFocusableElement, btn, i);
63                }
64            });
65            this.#closeDropdownOnBackdrop(btn, i);
66        });
67        this.#layer3rdBtns.forEach((btn, i) => {
68            btn.addEventListener('click', () => {
69                this.#onClickLayer3rdBtn(btn, i);
70            });
71        });
72    }
73    /** ヘッダーの高さを更新 */
74    #updataHeaderHeight() {
75        if (this.#menuWrap) {
76            this.#menuWrapHeight = this.#menuWrap.clientHeight;
77            this.#htmlElement?.style.setProperty(this.#cssVariablesName, `${this.#menuWrapHeight}px`);
78        }
79        // 高さが変わったらプロパティ更新
80        const resizeObserver = new ResizeObserver((entries) => {
81            const entry = entries[0];
82            if (!entry) {
83                return;
84            }
85            this.#menuWrapHeight = entry.borderBoxSize[0]?.blockSize;
86            this.#htmlElement?.style.setProperty(this.#cssVariablesName, `${this.#menuWrapHeight}px`);
87        });
88        if (this.#menuWrap) {
89            resizeObserver.observe(this.#menuWrap);
90        }
91    }
92    /**
93     * フォーカス制御
94     * @param lastEl header内でフォーカス可能な最後の要素
95     * @param firstEl mian内でフォーカス可能な最初の要素
96     * @param selectedBtn アクティブの第2階層ボタン
97     * @param i アクティブのボタンのindex
98     */
99    #setFocus(lastEl, firstEl, selectedBtn, i) {
100        lastEl.addEventListener('keydown', (e) => {
101            if (e.key === 'Tab' && !e.shiftKey) {
102                e.preventDefault();
103                this.#closeDropdown(selectedBtn, i);
104                lastEl?.blur();
105                firstEl.focus();
106            }
107        });
108    }
109    /**
110     * 第2階層ボタンのクリックイベント
111     * @param selectedBtn 選択されたボタン
112     * @param i 選択されたボタンのindex
113     */
114    #onClickLayer2ndBtn(selectedBtn, i) {
115        this.#bodyElement?.classList.add(this.#scrollLockClass);
116        this.#headerParent?.classList.add(this.#openClass);
117        if (this.#backdrop) {
118            this.#backdrop.classList.remove(this.#hiddenClass);
119        }
120        this.#layer3rdBtns.forEach((btn) => {
121            this.#setBtnInactive(btn);
122        });
123        this.#setMenusHidden(this.#menu4th);
124        this.#setBtnsState(this.#layer2ndBtns, selectedBtn);
125        this.#dropdownMenuArray[i]?.classList.remove(this.#hiddenClass);
126        this.#bindCloseDropdown(selectedBtn, i);
127    }
128    /** ドロップダウンを閉じる */
129    #bindCloseDropdown(selectedBtn, i) {
130        const closeBtn = this.#dropdownMenuArray[i]?.querySelector(this.#dropdownMenuCloseBtn);
131        if (closeBtn) {
132            closeBtn.addEventListener('click', () => {
133                this.#closeDropdown(selectedBtn, i);
134            });
135        }
136    }
137    /**
138     * 第3階層ボタンのクリックイベント
139     * @param selectedBtn 選択されたボタン
140     * @param i 選択されたボタンのindex
141     */
142    #onClickLayer3rdBtn(selectedBtn, i) {
143        this.#setBtnsState(this.#layer3rdBtns, selectedBtn);
144        this.#setMenusHidden(this.#menu4th);
145        this.#menu4thArray[i]?.classList.remove(this.#hiddenClass);
146    }
147    /**
148     * メニューを非表示にする
149     * @param targets
150     */
151    #setMenusHidden(targets) {
152        targets.forEach((target) => {
153            target.classList.add(this.#hiddenClass);
154        });
155    }
156    /**
157     * 選択されたボタンをアクティブにする
158     * @param btns ボタンリスト
159     * @param selecedBtn アクティブにするボタン
160     */
161    #setBtnsState(btns, selecedBtn) {
162        btns.forEach((btn) => {
163            this.#setBtnInactive(btn);
164        });
165        this.#setBtnActive(selecedBtn);
166    }
167    /**
168     * ボタンのアクティブをセット
169     * @param btn メニュー展開するボタン
170     */
171    #setBtnActive(btn) {
172        btn.classList.add(this.#activeClass);
173        btn.setAttribute('aria-expanded', 'true');
174    }
175    /**
176     * ボタンの非アクティブをセット
177     * @param btn
178     */
179    #setBtnInactive(btn) {
180        btn.classList.remove(this.#activeClass);
181        btn.setAttribute('aria-expanded', 'false');
182    }
183    /**
184     * バックドロップによるドロップダウンのクローズ
185     * @param selectedBtn アクティブのボタン
186     * @param i 表示中ドロップダウンのindex
187     */
188    #closeDropdownOnBackdrop(selectedBtn, i) {
189        this.#backdrop?.removeEventListener('click', () => {
190            this.#closeDropdown(selectedBtn, i);
191        });
192        this.#backdrop?.addEventListener('click', () => {
193            this.#closeDropdown(selectedBtn, i);
194        });
195    }
196    /**
197     * ドロップダウンのクローズ
198     * @param selectedBtn アクティブのボタン
199     * @param i 表示中ドロップダウンのindex
200     */
201    #closeDropdown(selectedBtn, i) {
202        this.#bodyElement?.classList.remove(this.#scrollLockClass);
203        this.#headerParent?.classList.remove(this.#openClass);
204        this.#setBtnInactive(selectedBtn);
205        this.#dropdownMenuArray[i]?.classList.add(this.#hiddenClass);
206        this.#backdrop?.classList.add(this.#hiddenClass);
207    }
208    /**
209     * 画面スクロールによるクラス付与(トップのみ)
210     */
211    #setClassByScrollY() {
212        window.addEventListener('scroll', () => {
213            if (this.#headerParent) {
214                this.#headerParent.classList.toggle(this.#scrollClass, window.scrollY > 0);
215            }
216        });
217    }
218}

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.