アコーディオン表示のテーブルのフィルターがうまく動作しない


### 実現したいこと

アコーディオン表示のテーブルに下記の動きをするフィルターを追加したい

  * 小カテゴリのA,B,C,Dのセレクトボックスで任意の名前を選択すると、  
名前が同じ行と選択した名前を含む中カテゴリのチーム名と大カテゴリのグループ名を表示

例： Cを選択した場合  
  
[![画像の説明をここに入力](https://i.stack.imgur.com/LtaPR.png)](https://i.stack.imgur.com/LtaPR.png)

### 困っていること

  * フィルターでB,C,Dを選択したときに不正表示になってしまう
  * フィルターの動作箇所の`while (nextRow && !nextRow.querySelector(".accordion-button")) {`と`if (nextRow.matches(".accordion-content")) {`が不正という認識ですが、どのようにすれば考えている表示ができるのかわからない状態

### 試したこと

  * コメントアウトの`フィルターの動作`部分を作成

※データについては固定  
どのように改善していいか検討もつかず困っています... どうかよろしくお願いいたします。

[code]

    data = [{
        "name": "グループ1",
        "time": "i分s秒",
        "child": [{
            "name": "チーム1",
            "time": "i分s秒",
            "grand": [{
                "name": "A",
                "time": "i分s秒"
              },
              {
                "name": "B",
                "time": "i分s秒"
              }
            ]
          },
          {
            "name": "チーム2",
            "time": "i分s秒",
            "grand": [{
                "name": "C",
                "time": "i分s秒"
              },
              {
                "name": "D",
                "time": "i分s秒"
              }
            ]
          }
        ]
      },
      {
        "name": "グループ2",
        "time": "i分s秒",
        "child": [{
            "name": "チーム3",
            "time": "i分s秒",
            "grand": [{
                "name": "A",
                "time": "i分s秒"
              },
              {
                "name": "C",
                "time": "i分s秒"
              }
            ]
          },
          {
            "name": "チーム4",
            "time": "i分s秒",
            "grand": [{
                "name": "B",
                "time": "i分s秒"
              },
              {
                "name": "D",
                "time": "i分s秒"
              }
            ]
          }
        ]
      }
    ];
    
    const tableBody = document.querySelector("table tbody");
    // テーブル作成
    data.forEach((item) => {
      const row = document.createElement("tr");
      row.innerHTML = `<td class="accordion-button">${item.name}</td>
                       <td>${item.time}</td>`;
      tableBody.appendChild(row);
      if (item.child) {
        item.child.forEach((classInfo) => {
          const classRow = document.createElement("tr");
          classRow.innerHTML = `<td class="accordion-child-button">${classInfo.name}</td>
                                <td>${classInfo.time}</td>`;
          tableBody.appendChild(classRow);
          classRow.classList.add('accordion-content');
          if (classInfo.grand) {
            classInfo.grand.forEach((grandInfo) => {
              const grandRow = document.createElement("tr");
              grandRow.innerHTML = `<td>${grandInfo.name}</td>
                                    <td>${grandInfo.time}</td>`;
              tableBody.appendChild(grandRow);
              grandRow.classList.add('accordion-content1');
            });
          }
        });
      }
    });
    
    
    const accordionButtons = document.querySelectorAll(".accordion-button");
    const accordionChildButtons = document.querySelectorAll(".accordion-child-button");
    
    // アコーディオン表示（大カテゴリ押下時）
    accordionButtons.forEach((button) => {
      button.addEventListener("click", () => {
        let childRow = button.parentElement.nextElementSibling;
        while (childRow && !childRow.querySelector(".accordion-button")) {
          if (childRow.matches(".show, .accordion-content"))
            childRow.classList.toggle("show");
          childRow = childRow.nextElementSibling;
        }
      });
    });
    // 大カテゴリ押下時（中カテゴリ押下時）
    accordionChildButtons.forEach((button) => {
      button.addEventListener("click", () => {
        let childRow = button.parentElement.nextElementSibling;
        while (childRow && childRow.matches(".accordion-content1")) {
          childRow.classList.toggle("show");
          childRow = childRow.nextElementSibling;
        }
      });
    });
    
    // フィルター部分のオプション切り出し
    const selectElement = document.getElementById("category");
    const grandNames = new Set();
    data.forEach((item) => {
      if (item.child) {
        item.child.forEach((classInfo) => {
          if (classInfo.grand) {
            classInfo.grand.forEach((grandInfo) => {
              grandNames.add(grandInfo.name);
            });
          }
        });
      }
    });
    
    // フィルター部分に表示内容を追加
    grandNames.forEach((grandName) => {
      const option = document.createElement("option");
      option.textContent = grandName;
      option.value = grandName;
      selectElement.appendChild(option);
    });
    
    const accordionContent1 = document.querySelectorAll(".accordion-content1");
    // フィルターの動作
    selectElement.addEventListener("change", () => {
      const selectedGrandName = selectElement.value;
      accordionContent1.forEach((row) => {
        const nameColumn = row.querySelector("td:first-child");
        if (selectedGrandName === nameColumn.textContent) {
          row.classList.toggle("show");
          let nextRow = row.previousElementSibling;
          while (nextRow && !nextRow.querySelector(".accordion-button")) {
            if (nextRow.matches(".accordion-content")) {
              nextRow.classList.toggle("show");
            }
            nextRow = nextRow.previousElementSibling;
          }
        }
      });
    });
[/code]

[code]

    <style>
    /* テーブルのスタイル */
    
    table {
      width: 100%;
      border-collapse: collapse;
    }
    
    th,
    td {
      border: 1px solid #dddddd;
      text-align: left;
      padding: 8px;
    }
    
    th {
      background-color: #f2f2f2;
    }
    
    
    /* アコーディオンボタンのスタイル */
    
    .accordion-button {
      cursor: pointer;
      padding: 8px;
      background-color: #5F9EA0;
      border: 1px solid #dddddd;
    }
    
    .accordion-child-button {
      cursor: pointer;
      padding: 8px;
      background-color: #B0E0E6;
      border: 1px solid #dddddd;
    }
    
    .accordion-content {
      display: none;
      padding: 8px;
    }
    
    .accordion-content1 {
      display: none;
      padding: 8px;
    }
    
    .show {
      display: table-row;
    }
    
    </style>
[/code]

[code]

    <label>フィルター</label>
    <select id="category">
      <option category_id=""></option>
    </select>
    <table id="table">
      <thead>
        <tr>
          <th>Name</th>
          <th>time</th>
        </tr>
      </thead>
      <tbody>
      </tbody>
    </table>
[/code]




一旦自己解決した為記載します。

>   * フィルターでB,C,Dを選択したときに不正表示になってしまう
>   * フィルターの動作箇所のwhile (nextRow && !nextRow.querySelector(".accordion-
> button")) {とif  
>  (nextRow.matches(".accordion-content"))  
>  {が不正という認識ですが、どのようにすれば考えている表示ができるのかわからない状態
>

不正表示になるのは、`if (nextRow.matches(".accordion-content"))
{`に複数回入っていることが原因でした。その為、`break;`を追加することで改善しました。

[code]

    data = [{
      "name": "グループ1",
      "time": "i分s秒",
      "child": [{
          "name": "チーム1",
          "time": "i分s秒",
          "grand": [{
              "name": "A",
              "time": "i分s秒"
            },
            {
              "name": "B",
              "time": "i分s秒"
            }
          ]
        },
        {
          "name": "チーム2",
          "time": "i分s秒",
          "grand": [{
              "name": "C",
              "time": "i分s秒"
            },
            {
              "name": "D",
              "time": "i分s秒"
            }
          ]
        }
      ]
    },
    {
      "name": "グループ2",
      "time": "i分s秒",
      "child": [
        {
          "name": "チーム3",
          "time": "i分s秒",
          "grand": [
            {
              "name": "A",
              "time": "i分s秒"
            },
            {
              "name": "C",
              "time": "i分s秒"
            }
          ]
        },
        {
          "name": "チーム4",
          "time": "i分s秒",
          "grand": [
            {
              "name": "B",
              "time": "i分s秒"
            },
            {
              "name": "D",
              "time": "i分s秒"
            }
          ]
        }
      ]
    }];
    
    const tableBody = document.querySelector("table tbody");
    // テーブル作成
    data.forEach((item) => {
      const row = document.createElement("tr");
      row.innerHTML = `<td class="accordion-button">${item.name}</td>
                       <td>${item.time}</td>`;
      tableBody.appendChild(row);
      if (item.child) {
        item.child.forEach((classInfo) => {
          const classRow = document.createElement("tr");
          classRow.innerHTML = `<td class="accordion-child-button">${classInfo.name}</td>
                                <td>${classInfo.time}</td>`;
          tableBody.appendChild(classRow);
          classRow.classList.add('accordion-content');
          if (classInfo.grand) {
            classInfo.grand.forEach((grandInfo) => {
              const grandRow = document.createElement("tr");
              grandRow.innerHTML = `<td>${grandInfo.name}</td>
                                    <td>${grandInfo.time}</td>`;
              tableBody.appendChild(grandRow);
              grandRow.classList.add('accordion-content1');
            });
          }
        });
      }
    });
    
    // フィルター部分のオプション切り出し
    const selectElement = document.getElementById("category");
    const grandNames = new Set();
    data.forEach((item) => {
      if (item.child) {
        item.child.forEach((classInfo) => {
          if (classInfo.grand) {
            classInfo.grand.forEach((grandInfo) => {
              grandNames.add(grandInfo.name);
            });
          }
        });
      }
    });
    
    // フィルター部分に表示内容を追加
    grandNames.forEach((grandName) => {
      const option = document.createElement("option");
      option.textContent = grandName;
      option.value = grandName;
      selectElement.appendChild(option);
    });
    
    const accordionButtons = document.querySelectorAll(".accordion-button");
    const accordionChildButtons = document.querySelectorAll(".accordion-child-button");
    const accordionContent1 = document.querySelectorAll(".accordion-content1");
    const tableRows = document.querySelectorAll("table tbody tr");
    let selectedGrandName;
    
    // 大カテゴリ押下時
    accordionButtons.forEach((button) => {
      button.addEventListener("click", () => {
        if(selectedGrandName){
          selectElement.options[0].selected = true;
          selectedGrandName = selectElement.value;
          removeShow();
        } else {
          let childRow = button.parentElement.nextElementSibling;
          while (childRow && !childRow.querySelector(".accordion-button")) {
            if (childRow.matches(".show, .accordion-content")){
              childRow.classList.toggle("show");
            }
            childRow = childRow.nextElementSibling;
          }
        }
      });
    });
    
    // 中カテゴリ押下時
    accordionChildButtons.forEach((button) => {
      button.addEventListener("click", () => {
        if(selectedGrandName){
          console.log(selectedGrandName);
          selectElement.options[0].selected = true;
          selectedGrandName = selectElement.value;
          removeShow();
        } else {
          let childRow = button.parentElement.nextElementSibling;
          while (childRow && childRow.matches(".accordion-content1")) {
            childRow.classList.toggle("show");
            childRow = childRow.nextElementSibling;
          }
        }
      });
    });
    
    // フィルターの動作
    selectElement.addEventListener("change", () => {
      selectedGrandName = selectElement.value;
      // テーブル初期化
      removeShow();
      // 表示対象が選択されている場合
      if (selectedGrandName) {
        accordionContent1.forEach((row) => {
          const nameColumn = row.querySelector("td:first-child");
          if (selectedGrandName === nameColumn.textContent) {
            toggleAccordionRows(row);
          }
        });
      }
    });
    
    // テーブル表示初期化
    function removeShow() {
      tableRows.forEach((row) => {
        row.classList.remove("show");
      });
    }
    
    // アコーディオン行のトグル
    function toggleAccordionRows(row) {
      row.classList.toggle("show");
      let nextRow = row.previousElementSibling;
      while (nextRow && !nextRow.querySelector(".accordion-button")) {
        if (nextRow.matches(".accordion-content")) {
          nextRow.classList.toggle("show");
          break;
        }
        nextRow = nextRow.previousElementSibling;
      }
    }
[/code]

[code]

    <style>
    /* テーブルのスタイル */
    
    table {
      width: 100%;
      border-collapse: collapse;
    }
    
    th,
    td {
      border: 1px solid #dddddd;
      text-align: left;
      padding: 8px;
    }
    
    th {
      background-color: #f2f2f2;
    }
    
    
    /* アコーディオンボタンのスタイル */
    
    .accordion-button {
      cursor: pointer;
      padding: 8px;
      background-color: #5F9EA0;
      border: 1px solid #dddddd;
    }
    
    .accordion-child-button {
      cursor: pointer;
      padding: 8px;
      background-color: #B0E0E6;
      border: 1px solid #dddddd;
    }
    
    .accordion-content {
      display: none;
      padding: 8px;
    }
    
    .accordion-content1 {
      display: none;
      padding: 8px;
    }
    
    .show {
      display: table-row;
    }
    </style>
[/code]

[code]

    <label>フィルター</label>
    <select id="category">
      <option category_id=""></option>
    </select>
    <table id="table">
      <thead>
        <tr>
          <th>Name</th>
          <th>time</th>
        </tr>
      </thead>
      <tbody>
      </tbody>
    </table>
[/code]




一旦自己解決した為記載します。

>   * フィルターでB,C,Dを選択したときに不正表示になってしまう
>   * フィルターの動作箇所のwhile (nextRow && !nextRow.querySelector(".accordion-
> button")) {とif  
>  (nextRow.matches(".accordion-content"))  
>  {が不正という認識ですが、どのようにすれば考えている表示ができるのかわからない状態
>

不正表示になるのは、`if (nextRow.matches(".accordion-content"))
{`に複数回入っていることが原因でした。その為、`break;`を追加することで改善しました。

[code]

    data = [{
      "name": "グループ1",
      "time": "i分s秒",
      "child": [{
          "name": "チーム1",
          "time": "i分s秒",
          "grand": [{
              "name": "A",
              "time": "i分s秒"
            },
            {
              "name": "B",
              "time": "i分s秒"
            }
          ]
        },
        {
          "name": "チーム2",
          "time": "i分s秒",
          "grand": [{
              "name": "C",
              "time": "i分s秒"
            },
            {
              "name": "D",
              "time": "i分s秒"
            }
          ]
        }
      ]
    },
    {
      "name": "グループ2",
      "time": "i分s秒",
      "child": [
        {
          "name": "チーム3",
          "time": "i分s秒",
          "grand": [
            {
              "name": "A",
              "time": "i分s秒"
            },
            {
              "name": "C",
              "time": "i分s秒"
            }
          ]
        },
        {
          "name": "チーム4",
          "time": "i分s秒",
          "grand": [
            {
              "name": "B",
              "time": "i分s秒"
            },
            {
              "name": "D",
              "time": "i分s秒"
            }
          ]
        }
      ]
    }];
    
    const tableBody = document.querySelector("table tbody");
    // テーブル作成
    data.forEach((item) => {
      const row = document.createElement("tr");
      row.innerHTML = `<td class="accordion-button">${item.name}</td>
                       <td>${item.time}</td>`;
      tableBody.appendChild(row);
      if (item.child) {
        item.child.forEach((classInfo) => {
          const classRow = document.createElement("tr");
          classRow.innerHTML = `<td class="accordion-child-button">${classInfo.name}</td>
                                <td>${classInfo.time}</td>`;
          tableBody.appendChild(classRow);
          classRow.classList.add('accordion-content');
          if (classInfo.grand) {
            classInfo.grand.forEach((grandInfo) => {
              const grandRow = document.createElement("tr");
              grandRow.innerHTML = `<td>${grandInfo.name}</td>
                                    <td>${grandInfo.time}</td>`;
              tableBody.appendChild(grandRow);
              grandRow.classList.add('accordion-content1');
            });
          }
        });
      }
    });
    
    // フィルター部分のオプション切り出し
    const selectElement = document.getElementById("category");
    const grandNames = new Set();
    data.forEach((item) => {
      if (item.child) {
        item.child.forEach((classInfo) => {
          if (classInfo.grand) {
            classInfo.grand.forEach((grandInfo) => {
              grandNames.add(grandInfo.name);
            });
          }
        });
      }
    });
    
    // フィルター部分に表示内容を追加
    grandNames.forEach((grandName) => {
      const option = document.createElement("option");
      option.textContent = grandName;
      option.value = grandName;
      selectElement.appendChild(option);
    });
    
    const accordionButtons = document.querySelectorAll(".accordion-button");
    const accordionChildButtons = document.querySelectorAll(".accordion-child-button");
    const accordionContent1 = document.querySelectorAll(".accordion-content1");
    const tableRows = document.querySelectorAll("table tbody tr");
    let selectedGrandName;
    
    // 大カテゴリ押下時
    accordionButtons.forEach((button) => {
      button.addEventListener("click", () => {
        if(selectedGrandName){
          selectElement.options[0].selected = true;
          selectedGrandName = selectElement.value;
          removeShow();
        } else {
          let childRow = button.parentElement.nextElementSibling;
          while (childRow && !childRow.querySelector(".accordion-button")) {
            if (childRow.matches(".show, .accordion-content")){
              childRow.classList.toggle("show");
            }
            childRow = childRow.nextElementSibling;
          }
        }
      });
    });
    
    // 中カテゴリ押下時
    accordionChildButtons.forEach((button) => {
      button.addEventListener("click", () => {
        if(selectedGrandName){
          console.log(selectedGrandName);
          selectElement.options[0].selected = true;
          selectedGrandName = selectElement.value;
          removeShow();
        } else {
          let childRow = button.parentElement.nextElementSibling;
          while (childRow && childRow.matches(".accordion-content1")) {
            childRow.classList.toggle("show");
            childRow = childRow.nextElementSibling;
          }
        }
      });
    });
    
    // フィルターの動作
    selectElement.addEventListener("change", () => {
      selectedGrandName = selectElement.value;
      // テーブル初期化
      removeShow();
      // 表示対象が選択されている場合
      if (selectedGrandName) {
        accordionContent1.forEach((row) => {
          const nameColumn = row.querySelector("td:first-child");
          if (selectedGrandName === nameColumn.textContent) {
            toggleAccordionRows(row);
          }
        });
      }
    });
    
    // テーブル表示初期化
    function removeShow() {
      tableRows.forEach((row) => {
        row.classList.remove("show");
      });
    }
    
    // アコーディオン行のトグル
    function toggleAccordionRows(row) {
      row.classList.toggle("show");
      let nextRow = row.previousElementSibling;
      while (nextRow && !nextRow.querySelector(".accordion-button")) {
        if (nextRow.matches(".accordion-content")) {
          nextRow.classList.toggle("show");
          break;
        }
        nextRow = nextRow.previousElementSibling;
      }
    }
[/code]

[code]

    <style>
    /* テーブルのスタイル */
    
    table {
      width: 100%;
      border-collapse: collapse;
    }
    
    th,
    td {
      border: 1px solid #dddddd;
      text-align: left;
      padding: 8px;
    }
    
    th {
      background-color: #f2f2f2;
    }
    
    
    /* アコーディオンボタンのスタイル */
    
    .accordion-button {
      cursor: pointer;
      padding: 8px;
      background-color: #5F9EA0;
      border: 1px solid #dddddd;
    }
    
    .accordion-child-button {
      cursor: pointer;
      padding: 8px;
      background-color: #B0E0E6;
      border: 1px solid #dddddd;
    }
    
    .accordion-content {
      display: none;
      padding: 8px;
    }
    
    .accordion-content1 {
      display: none;
      padding: 8px;
    }
    
    .show {
      display: table-row;
    }
    </style>
[/code]

[code]

    <label>フィルター</label>
    <select id="category">
      <option category_id=""></option>
    </select>
    <table id="table">
      <thead>
        <tr>
          <th>Name</th>
          <th>time</th>
        </tr>
      </thead>
      <tbody>
      </tbody>
    </table>
[/code]

