//@ts-check // -------------------------------------------------------------------------------- // 関数定義 // -------------------------------------------------------------------------------- /** @type {(category: string, method: string) => string} - "カテゴリー::術式" */ const getCaseIdentifier = (category, method) => `${category}::${method}`; // -------------------------------------------------------------------------------- // 定数定義 // -------------------------------------------------------------------------------- /** カテゴリー・術式選択ボタンを取得するCSSセレクタ */ const CSS_SELECTOR_SELECT_BUTTONS = "div.method_selectors button:is(.category_select_button, .method_select_button)"; /** 各症例に対応する要素を取得するCSSセレクタ */ const CSS_SELECTOR_CASE_ITEMS = "ul.cases li.case"; /** 症例要素内で症例のカテゴリーを取得するCSSセレクタ */ const CSS_SELECTOR_CASE_CATEGORY = "div.category_method_pills > span[data-type='category']"; /** 症例要素内で症例の術式を取得するCSSセレクタ */ const CSS_SELECTOR_CASE_METHOD = "div.category_method_pills > span[data-type='method']"; // -------------------------------------------------------------------------------- // ページのロード完了を待つ // -------------------------------------------------------------------------------- await /** @type {Promise} */ (new Promise(resolve => { if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => resolve()); } else { resolve(); } })); // -------------------------------------------------------------------------------- // 表示する術式を格納するセット // -------------------------------------------------------------------------------- const selectedMethods = new Set(); // -------------------------------------------------------------------------------- // 各症例の表示・非表示を切り替える関数 // -------------------------------------------------------------------------------- const updateCaseItemsVisibility = () => { // 各症例要素に対してループ処理 document.querySelectorAll(CSS_SELECTOR_CASE_ITEMS).forEach(caseItem => { if (!(caseItem instanceof HTMLElement)) return; // 症例のカテゴリーと術式を取得 const categoryElement = caseItem.querySelector(CSS_SELECTOR_CASE_CATEGORY); const methodElement = caseItem.querySelector(CSS_SELECTOR_CASE_METHOD); const category = categoryElement ? categoryElement.textContent.trim() : ""; const method = methodElement ? methodElement.textContent.trim() : ""; const caseIdentifier = getCaseIdentifier(category, method); // caseIdentifierがselectedMethodsに含まれているかどうかで表示・非表示を切り替え if (selectedMethods.has(caseIdentifier)) { caseItem.style.display = ""; // 表示 } else { caseItem.style.display = "none"; // 非表示 } }); // もし1件もヒットしない場合はul.cases>li.not_foundを表示、1件以上ヒットする場合は非表示 const anyVisible = Array.from(document.querySelectorAll(CSS_SELECTOR_CASE_ITEMS)).some(caseItem => { return caseItem instanceof HTMLElement && caseItem.style.display !== "none"; }); const notFoundElement = document.querySelector("ul.cases > li.not_found"); if (notFoundElement instanceof HTMLElement) { notFoundElement.style.display = anyVisible ? "none" : ""; // ヒットしない場合は表示、ヒットする場合は非表示 } // ul.casesに対して、もし1件もヒットしない場合はul.casesに.not_found_visibleクラスを追加、1件以上ヒットする場合は削除 const casesListElement = document.querySelector("ul.cases"); if (casesListElement instanceof HTMLElement) { if (anyVisible) { casesListElement.classList.remove("not_found_visible"); } else { casesListElement.classList.add("not_found_visible"); } } }; // -------------------------------------------------------------------------------- // 抽出ボタンにクリックイベントを設定 // -------------------------------------------------------------------------------- document.querySelectorAll(CSS_SELECTOR_SELECT_BUTTONS).forEach(buttonElement => { if (!(buttonElement instanceof HTMLButtonElement)) return; // カテゴリーボタンか術式ボタンかを判別 const isCategoryButton = buttonElement.classList.contains("category_select_button"); const isMethodButton = buttonElement.classList.contains("method_select_button"); if (!isCategoryButton && !isMethodButton) return; if (isCategoryButton) { // カテゴリーボタンがクリックされた場合 buttonElement.addEventListener("click", () => { // .latest_clickedクラスを削除 document.querySelectorAll(CSS_SELECTOR_SELECT_BUTTONS).forEach(btn => { btn.classList.remove("latest_clicked"); }); // クリックされたボタンに.latest_clickedクラスを追加 buttonElement.classList.add("latest_clicked"); const categoryName = buttonElement.textContent.trim(); // クリックされたカテゴリーに属する術式をすべて取得(兄弟要素で.method_select_buttonなもののtextContent) const methodNames = Array.from(buttonElement.parentElement?.querySelectorAll("button.method_select_button") || []).map(btn => btn.textContent.trim()).filter(name => name); // selectedMethodsを更新 selectedMethods.clear(); methodNames.forEach(methodName => { const caseIdentifier = getCaseIdentifier(categoryName, methodName); selectedMethods.add(caseIdentifier); }); // 症例の表示・非表示を更新 updateCaseItemsVisibility(); // h3#case_filter_titleに「(ボタンのテキスト)の症例」と表示 const titleElement = document.getElementById("case_filter_title"); if (titleElement) { titleElement.textContent = `${categoryName}の症例`; } }); } else if (isMethodButton) { // 術式ボタンがクリックされた場合 buttonElement.addEventListener("click", () => { // .latest_clickedクラスを削除 document.querySelectorAll(CSS_SELECTOR_SELECT_BUTTONS).forEach(btn => { btn.classList.remove("latest_clicked"); }); // クリックされたボタンに.latest_clickedクラスを追加 buttonElement.classList.add("latest_clicked"); const methodName = buttonElement.textContent.trim(); // クリックされた術式に対応するカテゴリー名を取得(兄弟要素で.category_select_buttonなもの(1個しかない)のtextContent) const categoryButton = buttonElement.parentElement?.querySelector("button.category_select_button"); const categoryName = categoryButton ? categoryButton.textContent.trim() : ""; if (!categoryName) return; const caseIdentifier = getCaseIdentifier(categoryName, methodName); // selectedMethodsを更新 selectedMethods.clear(); selectedMethods.add(caseIdentifier); // 症例の表示・非表示を更新 updateCaseItemsVisibility(); // h3#case_filter_titleに「(ボタンのテキスト)の症例」と表示 const titleElement = document.getElementById("case_filter_title"); if (titleElement) { titleElement.textContent = `${methodName}の症例`; } }); } }); // -------------------------------------------------------------------------------- // "すべて表示"ボタンにクリックイベントを設定 // -------------------------------------------------------------------------------- document.querySelector(".select_all_button")?.addEventListener("click", () => { selectedMethods.clear(); // .latest_clickedクラスを削除 document.querySelectorAll(CSS_SELECTOR_SELECT_BUTTONS).forEach(btn => { btn.classList.remove("latest_clicked"); }); document.querySelectorAll(CSS_SELECTOR_CASE_ITEMS).forEach(caseItem => { if (caseItem instanceof HTMLElement) { caseItem.style.display = ""; // すべて表示 } }); // h3#case_filter_titleに「すべての症例」と表示 const titleElement = document.getElementById("case_filter_title"); if (titleElement) { titleElement.textContent = "すべての症例"; } // ul.casesに対して.not_found_visibleクラスを削除 const casesListElement = document.querySelector("ul.cases"); if (casesListElement instanceof HTMLElement) { casesListElement.classList.remove("not_found_visible"); } // ul.cases>li.not_foundを非表示 const notFoundElement = document.querySelector("ul.cases > li.not_found"); if (notFoundElement instanceof HTMLElement) { notFoundElement.style.display = "none"; } });