96186


### 実現したいこと

テーブルのアコーディオン表示で、大カテゴリを押下すると中カテゴリ（child）が表示され、中カテゴリ（child）を押下すると小カテゴリ（grand）を表示したい。

### 困っていること

大カテゴリを押下すると中カテゴリは表示されますが、中カテゴリを押下しても小カテゴリが表示されない。

### 試したこと

  * JSの動作を確認したところ、大カテゴリを押下して中カテゴリを表示する際に小カテゴリに`accordion-content`クラスが追加され実現したい動きをしていないを確認。
  * `while (childRow && !childRow.querySelector(".accordion-button")) {`の条件に`&& !childRow.querySelector(".accordion-content1")`を追記してみましたが、動きは改善しませんでした。

宜しくお願い致します。

[code]

    data = [{
      "name": "カテゴリ1",
      "time": "i分s秒",
      "child": [{
          "name": "カテゴリ1-1",
          "time": "i分s秒",
          "grand": [{
              "name": "カテゴリ1-1-1",
              "time": "i分s秒"
            },
            {
              "name": "カテゴリ1-1-2",
              "time": "i分s秒"
            }
          ]
        },
        {
          "name": "カテゴリ1-2",
          "time": "i分s秒",
          "grand": [{
              "name": "カテゴリ1-2-1",
              "time": "i分s秒"
            },
            {
              "name": "カテゴリ1-2-2",
              "time": "i分s秒"
            }
          ]
        },
        {
          "name": "カテゴリ1-3",
          "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")) {
          childRow.classList.toggle("accordion-content");
          childRow = childRow.nextElementSibling;
        }
      });
    });
    
    accordionChildButtons.forEach((button) => {
      button.addEventListener("click", () => {
        let childRow = button.parentElement.nextElementSibling;
        while (childRow && !childRow.querySelector(".accordion-child-button")) {
          childRow.classList.toggle("accordion-content1");
          childRow = childRow.nextElementSibling;
        }
      });
    });
[/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;
    }
    
    </style>
[/code]

[code]

    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>time</th>
        </tr>
      </thead>
      <tbody>
      </tbody>
    </table>
[/code]


CC BY-SA 4.0
2023-09-11T13:55:45.373
96181
2023-09-12T05:09:13.837
2023-09-12T05:09:13.837
3060
59677
question
javascript html
入れ子にしたテーブルのアコーディオン表示がうまくいかない


> `&& !childRow.querySelector(".accordion-content1")` を追記してみましたが、動きは改善しませんでした。

`childRow.querySelector(...)` は `childRow`
にはマッチしないので、`childRow.matches(".accordion-content1")` とする必要があります。  
これで改善はしますが、何度もクリックしていると子の行と孫の行が区別できなくなり期待通り動かなくなると思います。

親の行、子の行、孫の行を常に簡単に区別できるように、`accordion-content` と `accordion-content1`
は付け外ししないで、別の方法で表示非表示を切り替えるのはいかがでしょうか。

[code]

    data = [{
      "name": "カテゴリ1",
      "time": "i分s秒",
      "child": [{
          "name": "カテゴリ1-1",
          "time": "i分s秒",
          "grand": [{
              "name": "カテゴリ1-1-1",
              "time": "i分s秒"
            },
            {
              "name": "カテゴリ1-1-2",
              "time": "i分s秒"
            }
          ]
        },
        {
          "name": "カテゴリ1-2",
          "time": "i分s秒",
          "grand": [{
              "name": "カテゴリ1-2-1",
              "time": "i分s秒"
            },
            {
              "name": "カテゴリ1-2-2",
              "time": "i分s秒"
            }
          ]
        },
        {
          "name": "カテゴリ1-3",
          "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;
        }
      });
    });
[/code]

[code]

    /* テーブルのスタイル */
    
    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;
    }
[/code]

[code]

    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>time</th>
        </tr>
      </thead>
      <tbody>
      </tbody>
    </table>
[/code]


CC BY-SA 4.0
2023-09-11T22:46:05.433
96186
2023-09-11T23:52:08.880
2023-09-11T23:52:08.880
3475
3475
96181
answer