PageSourceSearch

https://www.tokyodome-hotels.co.jp/assets/common/scripts/modules/filter.js

js tokyodome-hotels.co.jp collected 2026-09-24 10:46:21 UTC 11,072 bytes, 339 lines download raw bytes

1import { ShowMore } from './showMore.js';
2/**
3 * 絞り込み
4 */
5export class Filter {
6    #hiddenClass = 'is-hidden';
7    #shownClass = 'is-shown';
8    #activeClass = 'is-active';
9    #pulldown;
10    #pulldownBtn;
11    #selectedPulldownValue;
12    #filterBtns;
13    #filterItems;
14    #filterItemLabelsMap = new Map();
15    #filterItemLabelList;
16    #currentItems;
17    #filterEmpty;
18    #showMoreTarget;
19    #showMore;
20    #urlQueryString;
21    #urlParams = new URLSearchParams();
22    #urlParamTabValue;
23    #urlParamSelectValue;
24    constructor(root) {
25        this.#pulldown = root.querySelector('.fn-pulldown-menu');
26        this.#pulldownBtn = root.querySelector('.fn-filter-btn');
27        this.#selectedPulldownValue = '';
28        this.#filterItemLabelList = [];
29        this.#filterBtns = root.querySelectorAll('.fn-tab-btn');
30        this.#filterItems = root.querySelectorAll('.fn-filter-item');
31        this.#currentItems = [];
32        this.#filterEmpty = root.querySelector('.fn-filter-empty');
33        this.#showMoreTarget = root.querySelector('.js-show-more');
34        this.#showMore = this.#showMoreTarget
35            ? new ShowMore(this.#showMoreTarget)
36            : null;
37        this.#urlQueryString = location.search;
38        this.#urlParams = new URLSearchParams(this.#urlQueryString);
39        this.#urlParamTabValue = this.#urlParams.get('tab') ?? '';
40        this.#urlParamSelectValue = this.#urlParams.get('select') ?? '';
41        this.#buildItemLabelsMap();
42        this.#setInitFilterBtn();
43        this.#updateEmptyState();
44        if (this.#showMore) {
45            this.#resetCurrentItems();
46            this.#showMore.currentItems = this.#currentItems;
47            this.#showMore.init();
48        }
49        this.#bindFilterTabEvents();
50        this.#bindFilterPulldownEvents();
51        this.#initFilterFromQuery();
52    }
53    /**
54     * 絞り込み共通の処理
55     */
56    /**
57     * 各アイテムが持つフィルターラベルをMapにキャッシュして保持
58     */
59    #buildItemLabelsMap() {
60        this.#filterItems.forEach((item) => {
61            const filterItemLabels = item.querySelectorAll('.fn-filter-label');
62            this.#filterItemLabelsMap.set(item, filterItemLabels);
63        });
64    }
65    /**
66     * #currentItemsを初期化
67     */
68    #resetCurrentItems() {
69        this.#currentItems = [...this.#filterItems];
70    }
71    /**
72     * #currentItemsを更新
73     */
74    #updateCurrentItems() {
75        this.#currentItems = this.#currentItems.filter((item) => !item.classList.contains(this.#hiddenClass));
76    }
77    #setShowMoreBtn() {
78        if (this.#showMore) {
79            this.#showMore.currentItems = this.#currentItems;
80            this.#showMore.changedFilterEvent();
81        }
82    }
83    /**
84     * 表示アイテム数が0件の場合の処理
85     * @returns
86     */
87    #updateEmptyState() {
88        if (!this.#filterEmpty) {
89            return;
90        }
91        const visibleItems = Array.from(this.#filterItems).filter((item) => !item.classList.contains(this.#hiddenClass));
92        this.#filterEmpty.classList.toggle(this.#shownClass, visibleItems.length === 0);
93    }
94    /**
95     * タブ関連処理
96     */
97    #setInitFilterBtn() {
98        if (!this.#filterBtns.length) {
99            return;
100        }
101        this.#filterBtns.forEach((btn) => {
102            const btnData = btn.dataset.category;
103            btn.classList.toggle(this.#activeClass, btnData === 'all');
104        });
105    }
106    #toggleCurrentBtn(selectedBtn) {
107        this.#filterBtns.forEach((btn) => {
108            btn.classList.toggle(this.#activeClass, btn === selectedBtn);
109        });
110    }
111    #getBtnAttr(selectedBtn) {
112        return selectedBtn.getAttribute('data-category') ?? '';
113    }
114    /**
115     * アイテムの表示制御
116     * @param selectedBtnCategory 選択されたボタンのdata-category属性
117     */
118    #filterTab(selectedBtnCategory) {
119        this.#currentItems.forEach((item) => {
120            const labels = this.#filterItemLabelsMap.get(item);
121            this.#filterItemLabelList = [];
122            if (!labels) {
123                return;
124            }
125            for (const label of labels) {
126                const itemCategory = label.getAttribute('data-filter-category') ?? '';
127                if (itemCategory) {
128                    this.#filterItemLabelList.push(itemCategory);
129                }
130            }
131            if (selectedBtnCategory === 'all' ||
132                this.#filterItemLabelList.includes(selectedBtnCategory)) {
133                item.classList.remove(this.#hiddenClass);
134            }
135            else {
136                item.classList.add(this.#hiddenClass);
137            }
138        });
139    }
140    /**
141     * 絞り込み
142     * @param btn 選択されたボタン
143     */
144    #bindFilterTab(btn) {
145        this.#resetCurrentItems();
146        if (this.#pulldown && this.#pulldownBtn) {
147            this.#selectedPulldownValue = this.#pulldown.value;
148            this.#filterPulldown();
149            this.#updateCurrentItems();
150        }
151        this.#toggleCurrentBtn(btn);
152        const selectedBtnCategory = this.#getBtnAttr(btn);
153        this.#filterTab(selectedBtnCategory);
154        this.#updateCurrentItems();
155        this.#setShowMoreBtn();
156        this.#updateEmptyState();
157    }
158    /**
159     * タブボタンによる絞り込み処理まとめ
160     */
161    #bindFilterTabEvents() {
162        this.#filterBtns.forEach((btn) => {
163            btn.addEventListener('click', () => {
164                this.#bindFilterTab(btn);
165            });
166        });
167    }
168    /**
169     * プルダウン関連処理
170     */
171    /**
172     * アイテムの表示制御
173     */
174    #filterPulldown() {
175        this.#currentItems.forEach((item) => {
176            const labels = this.#filterItemLabelsMap.get(item);
177            this.#filterItemLabelList = [];
178            if (!labels) {
179                return;
180            }
181            for (const label of labels) {
182                const value = label.getAttribute('data-filter-value') ?? '';
183                if (value) {
184                    this.#filterItemLabelList.push(value);
185                }
186            }
187            if (this.#selectedPulldownValue === 'all' ||
188                this.#filterItemLabelList.includes(this.#selectedPulldownValue)) {
189                item.classList.remove(this.#hiddenClass);
190            }
191            else {
192                item.classList.add(this.#hiddenClass);
193            }
194        });
195    }
196    /**
197     * プルダウン切替時
198     */
199    #bindFilterPulldown() {
200        this.#resetCurrentItems();
201        if (this.#filterBtns) {
202            this.#filterBtns.forEach((btn) => {
203                if (btn.classList.contains(this.#activeClass)) {
204                    const selectedBtnCategory = this.#getBtnAttr(btn);
205                    this.#filterTab(selectedBtnCategory);
206                }
207            });
208            this.#updateCurrentItems();
209        }
210        this.#filterPulldown();
211        this.#updateCurrentItems();
212        this.#setShowMoreBtn();
213        this.#updateEmptyState();
214    }
215    /**
216     * プルダウンによる絞り込み処理まとめ
217     */
218    #bindFilterPulldownEvents() {
219        this.#pulldown?.addEventListener('change', () => {
220            if (this.#pulldown) {
221                this.#selectedPulldownValue = this.#pulldown.value;
222            }
223        });
224        this.#pulldownBtn?.addEventListener('click', this.#bindFilterPulldown.bind(this));
225    }
226    #initFilterFromQuery() {
227        if (!this.#urlQueryString) {
228            return;
229        }
230        const matchedFilterTabBtn = Array.from(this.#filterBtns).some((btn) => this.#getBtnAttr(btn) === this.#urlParamTabValue);
231        this.#resetCurrentItems();
232        if (this.#pulldown) {
233            const matchedSelectOption = Array.from(this.#pulldown.options).some((opt) => opt.value === this.#urlParamSelectValue);
234            if (matchedSelectOption) {
235                this.#selectedPulldownValue = this.#pulldown.value =
236                    this.#urlParamSelectValue;
237                this.#filterPulldown();
238                this.#updateCurrentItems();
239            }
240            else {
241                this.#updateCurrentItems();
242                this.#setShowMoreBtn();
243                this.#updateEmptyState();
244            }
245        }
246        if (matchedFilterTabBtn) {
247            this.#filterBtns.forEach((btn) => {
248                const btnData = this.#getBtnAttr(btn);
249                btn.classList.toggle(this.#activeClass, btnData === this.#urlParamTabValue);
250            });
251            this.#filterTab(this.#urlParamTabValue);
252            this.#updateCurrentItems();
253            this.#setShowMoreBtn();
254            this.#updateEmptyState();
255        }
256        else {
257            this.#updateCurrentItems();
258            this.#setShowMoreBtn();
259            this.#updateEmptyState();
260        }
261    }
262}
263/**
264 * 実行
265 */
266/**
267 * タブのみ絞り込み
268 */
269document.addEventListener('DOMContentLoaded', () => {
270    const root = document.querySelector('.js-filter');
271    if (!root) {
272        return;
273    }
274    new Filter(root);
275});
276/**
277 * レストランフェア
278 */
279document.addEventListener('DOMContentLoaded', () => {
280    const root = document.querySelector('.js-filter-restaurant');
281    if (!root) {
282        return;
283    }
284    new Filter(root);
285});
286/**
287 * プレスリリース(独立)
288 */
289const hiddenClass = 'is-hidden';
290// プルダウンで選択された項目のvalueを取得
291const getpulldownMenuValue = (selectEl) => {
292    const pulldownMenuValue = selectEl.value;
293    return pulldownMenuValue;
294};
295// アイテムのdatetime属性の'年'を取得
296const getItemDateYear = (el) => {
297    const itemDateEl = el.querySelector('time');
298    const itemDateAttr = itemDateEl?.getAttribute('datetime');
299    if (!itemDateAttr) {
300        return;
301    }
302    const itemDateObject = new Date(itemDateAttr);
303    const itemDateYear = itemDateObject.getFullYear().toString();
304    return itemDateYear;
305};
306document.addEventListener('DOMContentLoaded', () => {
307    const root = document.querySelector('.js-filter-press');
308    if (!root) {
309        return;
310    }
311    const filterMenu = root.querySelector('.fn-pulldown-menu');
312    const filterBtn = root.querySelector('.fn-filter-btn');
313    if (!filterMenu || !filterBtn) {
314        return;
315    }
316    const filterItems = root.querySelectorAll('.fn-filter-item');
317    // アイテムの表示・非表示を切り替え
318    const toggleItems = (selectMenu, item) => {
319        if (getpulldownMenuValue(selectMenu) === 'all') {
320            item.classList.remove(hiddenClass);
321        }
322        else {
323            item.classList.toggle(hiddenClass, getpulldownMenuValue(selectMenu) !== getItemDateYear(item));
324        }
325    };
326    // 初期状態
327    const init = () => {
328        filterItems.forEach((item) => {
329            toggleItems(filterMenu, item);
330        });
331    };
332    init();
333    filterMenu.addEventListener('change', () => {
334        getpulldownMenuValue(filterMenu);
335    });
336    filterBtn.addEventListener('click', () => {
337        init();
338    });
339});

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.