Issues with displaying nested HTML tables properly

Struggling to set up a system for Parent and Child rows in an HTML table. While the code is somewhat functional, my lack of JavaScript knowledge is causing issues with selecting elements correctly. When I click on the first parent row, only one child row appears instead of both. And when I click on the second parent row, it displays the first child row of the first parent.

I have a strong feeling that the problem lies within the JavaScript portion of the code.

var toggler = document.getElementsByClassName("parent");
var i;
for (i = 0; i < toggler.length; i++) {
  toggler[i].addEventListener("click", function() {
    this.parentElement.querySelector(".child").classList.toggle("active");
    this.classList.toggle("parent-down");
    this.parentElement.querySelector(".arrow").classList.toggle("arrow-down ");
  });
}
.parent {
  cursor: pointer;
  user-select: none;
  /* Prevent text selection */
  font-size: 16px;
}

.arrow-down::before {
  -ms-transform: rotate(90deg);
  /* IE 9 */
  -webkit-transform: rotate(90deg);
  /* Safari */
  '
 transform: rotate(90deg);
}

.parent-down {
  border: 2px solid rgb(21, 67, 96);
  font-weight: bold;
}


/* Hide the child list */

.child {
  display: none;
  background-color: rgb(240, 250, 255);
  font-size: 14px;
}

.active {
  display: table-row;
}

.arrow::before {
  content: "\25B6";
  color: black;
  display: inline-block;
  margin-right: 6px;
}
<table>
  <tr>
    <th>Word</th>
    <th>Number of Letters</th>
    <th>Do I like the word?</th>
  </tr>
  <tr class="parent">
    <td class="arrow">Long Words</td>
    <td>-</td>
    <td>-</td>
  </tr>
  <tr class="child">
    <td>Bamboozle</td>
    <td>9</td>
    <td>Yes.</td>
  </tr>
  <tr class="child">
    <td>Peritoneum</td>
    <td>10</td>
    <td>No.</td>
  </tr>
  <tr class="parent">
    <td class="arrow">Short Words</td>
    <td>-</td>
    <td>-</td>
  </tr>
  <tr class="child">
    <td>Squeak</td>
    <td>6</td>
    <td>Yes.</td>
  </tr>
</table>

Answer №1

To navigate through your HTML structure and target specific elements, you can utilize the nextElementSibling property within a while loop. Make sure to include an emergency exit plan in case of any unexpected issues.

It's important to note that spaces are not allowed in class toggles.

I highly recommend revising your HTML layout by placing collapse buttons outside tables. Tables are best suited for tabular data, not for buttons or layout functionalities. This will significantly improve accessibility, especially for text-based browsers used by individuals with visual impairments.

By restructuring your elements appropriately, selecting the correct parent elements becomes much easier, eliminating the need for complex while loops.

var toggler = document.getElementsByClassName("parent");
var i;
for (i = 0; i < toggler.length; i++) {
  toggler[i].addEventListener("click", function() {
    var nextSibling = this.nextElementSibling;
    while(nextSibling !== null && nextSibling.classList.contains("child")) {
      nextSibling.classList.toggle("active");
      nextSibling = nextSibling.nextElementSibling;
    }
    this.classList.toggle("parent-down");
    this.querySelector(".arrow").classList.toggle("arrow-down");
  });
}
.parent {
  cursor: pointer;
  user-select: none;
  /* Prevent text selection */
  font-size: 16px;
}

.arrow-down::before {
  -ms-transform: rotate(90deg);
  /* IE 9 */
  -webkit-transform: rotate(90deg);
  /* Safari */
  transform: rotate(90deg);
}

.parent-down {
  border: 2px solid rgb(21, 67, 96);
  font-weight: bold;
}

/* Hide the child list */

.child {
  display: none;
  background-color: rgb(240, 250, 255);
  font-size: 14px;
}

.active {
  display: table-row;
}

.arrow::before {
  content: "\25B6";
  color: black;
  display: inline-block;
  margin-right: 6px;
}
<table>
  <tr>
    <th>Word</th>
    <th>Number of Letters</th>
    <th>Do I like the word?</th>
  </tr>
  <tr class="parent">
    <td class="arrow">Long Words</td>
    <td>-</td>
...
  
    </tbody>
  </table>

Answer №2

Iterate through a loop until the nextElementSibling no longer contains the .child class.

var parentElements = document.getElementsByClassName("parent");
var index;
for (index = 0; index < parentElements.length; index++) {
  parentElements[index].addEventListener("click", function() {
    this.parentElement.querySelector(".arrow").classList.toggle("arrow-down");
    let nextElement = this.nextElementSibling;
    while(nextElement && nextElement.classList.contains('child')) {
      nextElement.classList.toggle('active');
      this.classList.toggle("parent-down");
      nextElement = nextElement.nextElementSibling;
    }
  })
};
.parent {
  cursor: pointer;
  user-select: none;
  /* Prevent text selection */
  font-size: 16px;
}

.arrow-down::before {
  -ms-transform: rotate(90deg);
  -webkit-transform: rotate(90deg);
  transform: rotate(90deg);
}

.parent-down {
  border: 2px solid rgb(21, 67, 96);
  font-weight: bold;
}


/* Hide the child list */

.child {
  display: none;
  background-color: rgb(240, 250, 255);
  font-size: 14px;
}

.active {
  display: table-row;
}

.arrow::before {
  content: "\25B6";
  color: black;
  display: inline-block;
  margin-right: 6px;
}
<table>
  <tr>
    <th>Word</th>
    <th>Number of Letters</th>
    <th>Do I like the word?</th>
  </tr>
  <tr class="parent">
    <td class="arrow">Long Words</td>
    <td>-</td>
    <td>-</td>
  </tr>
  <tr class="child">
    <td>Bamboozle</td>
    <td>9</td>
    <td>Yes.</td>
  </tr>
  <tr class="child">
    <td>Peritoneum</td>
    <td>10</td>
    <td>No.</td>
  </tr>
  <tr class="parent">
    <td class="arrow">Short Words</td>
    <td>-</td>
    <td>-</td>
  </tr>
  <tr class="child">
    <td>Squeak</td>
    <td>6</td>
    <td>Yes.</td>
  </tr>
</table>

Answer №3

The significant modification I implemented involved assigning an ID to the parent elements and using the same ID as a class for their child elements.

var togglers = document.querySelectorAll(".parent");
togglers.forEach(function(toggler) {
  toggler.addEventListener("click", function() {
    document.querySelectorAll("." + this.id).forEach(function (child) {
    child.classList.toggle("active");
    })
    this.classList.toggle("parent-down");
    this.querySelector(".arrow").classList.toggle("arrow-down");
  })
})
.parent {
 cursor: pointer;
 user-select: none; /* Prevent text selection */
 font-size: 16px;
 }

.arrow-down::before {
  -ms-transform: rotate(90deg); /* IE 9 */
  -webkit-transform: rotate(90deg); /* Safari */
  transform: rotate(90deg);
}

.parent-down {
  border: 2px solid rgb(21, 67, 96);
  font-weight: bold;
}

/* Hide the child list */
.child {
  display: none;
  background-color: rgb(240, 250, 255);
  font-size: 14px;
}

.active {
  display: table-row;
}

.arrow::before {
  content: "\25B6";
  color: black;
  display: inline-block;
  margin-right: 6px;
}
<table>
 <tr>
  <th>Word</th>
   <th>Number of Letters</th> 
   <th>Do I like the word?</th>    
 </tr>
  <tr id="parent01" class = "parent">
   <td class = "arrow">Long Words</td> 
   <td>-</td>
   <td>-</td>
   </tr>
   <tr class = "child parent01">
    <td>Bamboozle</td> 
    <td>9</td>
    <td>Yes.</td>
   </tr>
   <tr class = "child parent01">
    <td>Peritoneum</td> 
    <td>10</td>
    <td>No.</td>
   </tr>      
   <tr id="parent02" class = "parent">
    <td class = "arrow">Short Words</td> 
    <td>-</td>
    <td>-</td>
   </tr>
   <tr class = "child parent02">
    <td>Squeak</td> 
    <td>6</td>
    <td>Yes.</td>
   </tr>                  
</table>

Answer №4

You have the option to tackle this using a `forEach` loop and utilizing `ES6` syntax

let toggler = document.querySelectorAll(".parent");

toggler.forEach(element => {
    element.addEventListener("click", function() {
        let nextSibling = this.nextElementSibling;
        while(nextSibling != null && nextSibling.classList.contains("child")) {
            nextSibling.classList.toggle("active");
            nextSibling = nextSibling.nextElementSibling;
        }
        this.classList.toggle("parent-down");
        this.querySelector(".arrow").classList.toggle("arrow-down");
    });
});
.parent {
  cursor: pointer;
  user-select: none;
  /* Prevent text selection */
  font-size: 16px;
}

.arrow-down::before {
  -ms-transform: rotate(90deg);
  /* IE 9 */
  -webkit-transform: rotate(90deg);
  /* Safari */
  transform: rotate(90deg);
}

.parent-down {
  border: 2px solid rgb(21, 67, 96);
  font-weight: bold;
}


/* Hide the child list */

.child {
  display: none;
  background-color: rgb(240, 250, 255);
  font-size: 14px;
}

.active {
  display: table-row;
}

.arrow::before {
  content: "\25B6";
  color: black;
  display: inline-block;
  margin-right: 6px;
}
<table>
  <tr>
    <th>Word</th>
    <th>Number of Letters</th>
    <th>Do I like the word?</th>
  </tr>
  <tr class="parent">
    <td class="arrow">Long Words</td>
    <td>-</td>
    <td>-</td>
  </tr>
  <tr class="child">
    <td>Bamboozle</td>
    <td>9</td>
    <td>Yes.</td>
  </tr>
  <tr class="child">
    <td>Peritoneum</td>
    <td>10</td>
    <td>No.</td>
  </tr>
  <tr class="parent">
    <td class="arrow">Short Words</td>
    <td>-</td>
    <td>-</td>
  </tr>
  <tr class="child">
    <td>Squeak</td>
    <td>6</td>
    <td>Yes.</td>
  </tr>
</table>
 Run code snippet

Answer №5

let selectors = document.getElementsByClassName("parent");
let i;
for (i = 0; i < selectors.length; i++) {
  selectors[i].addEventListener("click", function() {
    this.classList.toggle("active");
    chooseNextSibling(this);
  });
}

let chooseNextSibling = function(element) {
  if (!element.nextElementSibling || element.nextElementSibling.classList.contains("parent")) return false;
  element.nextElementSibling.classList.toggle("active-row");
  return arguments.callee(element.nextElementSibling);
}
.parent {
  cursor: pointer;
  user-select: none;
  /* Prevent text selection */
  font-size: 16px;
}

.active .arrow::before {
  -ms-transform: rotate(90deg);
  /* IE 9 */
  -webkit-transform: rotate(90deg);
  /* Safari */
  transform: rotate(90deg);
}


/* Hide the child list */

.child {
  background-color: rgb(240, 250, 255);
  font-size: 14px;
  display: none;
}

.active {
  color: red;
}

.active-row {
  display: table-row;
}

.arrow::before {
  content: "\25B6";
  color: black;
  display: inline-block;
  margin-right: 6px;
}
<table>
  <tr>
    <th>Word</th>
    <th>Number of Letters</th>
    <th>Do I like the word?</th>
  </tr>
  <tr class="parent">
    <td class="arrow">Long Words</td>
    <td>-</td>
    <td>-</td>
  </tr>
  <tr class="child">
    <td>Bamboozle</td>
    <td>9</td>
    <td>Yes.</td>
  </tr>
  <tr class="child">
    <td>Peritoneum</td>
    <td>10</td>
    <td>No.</td>
  </tr>
  <tr class="parent">
    <td class="arrow">Short Words</td>
    <td>-</td>
    <td>-</td>
  </tr>
  <tr class="child">
    <td>Squeak</td>
    <td>6</td>
    <td>Yes.</td>
  </tr>
</table>

Utilize nextElementSibling for selecting the adjacent sibling class ".child". Take a look at the snippet.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can a React project be executed without using the command npm start?

I find myself in a bit of a bind. I work part-time for a company that is looking to incorporate a new web technology called "web-component" into their website. They have recently adopted AngularJS (the first version) and I expressed my concerns about usin ...

Is there a way to retrieve the HTML raw code using backticks for string interpolation in JavaScript?

I am working on a resume builder project where the HTML template file is stored in Firebase storage and retrieved using a URL. The template has been modified to include string interpolation, such as <h1>${name}</h1>. However, when I fetch the d ...

The ValidationPipe in NestJS seems to be facing issues specifically with @Query() parameters

I've been attempting to convert certain query parameters from string to integer using the built-in NestJS ValidationPipe, but unfortunately, it doesn't appear to be functioning correctly. Below is my controller : import { ..., ValidationPipe } f ...

Implementing a PHP script to prompt the user to input two random integers and sum them up on a webpage through $_POST method

My current project involves creating an interactive adding game using PHP and HTML. In this game, the user is required to guess the correct sum of two randomly generated integers. However, I am encountering an issue where each time the user submits their a ...

Dealing with a Node and Express server can be tricky, especially when trying to proxy a POST request along with parameters. You might encounter the error

I am trying to forward all requests made to /api/ from my local node server to a remote server, while also adding some authentication parameters to them. Everything works smoothly for GET requests with query parameters and POST requests without specifying ...

Struggling with setting up Chrome options using Selenium WebDriver in JavaScript?

Encountering an issue while attempting to access a specific website using Selenium WebDriver in JavaScript with the Chrome browser. In the provided code snippet, the browser driver is initialized; however, there seems to be a problem when utilizing setChro ...

Handle form submission response from an express server using JavaScript

(Please help me with my terminology if it's incorrect.) The files server.js, run.js and index.html are all located in the same directory. server.js This file is responsible for setting up the server. const express = require('express'); ...

V-Calendar is not displaying the accurate dates

https://i.stack.imgur.com/zk4h7.png The image displays dates starting on June 1, 2022, which should be a Wednesday but appears as a Sunday on the calendar. This issue affects all months as they start on a Sunday instead of their respective weekdays. The p ...

Steps for generating an order from a shopping cart in Mongoose and removing the cart from the system

In my current task, I need to: Generate an order based on the items in my shopping cart Include the relevant object attributes in the order response based on the selection in the "select" option Ultimately, I aim to clear or delete the cart once the order ...

Why is it not possible to pass references when utilizing a self-invoking function?

I have been experimenting with the IIFE pattern for some of my modules lately and encountered a problem that has stumped me. In my current project, I need to pass a few global variables for usage. One of these is the global googletag variable which initial ...

Encountered an error while trying to filter markers and list using Knockout and Google Maps - Uncaught TypeError: Unable to locate property 'marker' of undefined

I am looking to enhance user experience by implementing a feature where clicking on a marker or a city name will display the info.window specific to that marker. HTML: <!DOCTYPE html> <html> <head> <title>Exploring Mag ...

Complete the form and send it to two separate locations

I'm encountering an issue with submitting a form to a page on my domain and then automatically resubmitting it to a different domain. Even though the code below successfully changes the form action and removes the ID, it fails to resubmit. It seems l ...

My Discord.JS bot seems to be moving at a snail's pace, and I can

Apologies for the lackluster title, I'm struggling to come up with something better. I'm currently running a self-bot (I understand it goes against the terms of service but I'm experimenting) that needs to download new files (specifically i ...

It is impossible to perform both actions at the same time

Is it possible to create a progress bar using data attributes in JQuery? Unfortunately, performing both actions simultaneously seems to be an issue. <div class="progressbar"> <div class="progressvalue" data-percent="50"></div> </d ...

Combining various JavaScript methods allows for the creation of complex

axis.remove(xaxis).remove(yaxis).remove(zaxis); Is there a way to condense these three removals into a single line (perhaps with regex)? This code snippet is related to the three.js library. ...

Can you explain the purpose of the text-align property set to justify-all?

Can you explain what the CSS property text-align: justify-all does? According to MDN, it should justify even the content of the last line. However, I am not seeing any changes in my Chrome browser: <p style="text-align: justify-all"> one two thre ...

Is it possible to apply styling to an element based on the position property of its ancestors using only CSS?

Wondering if it's possible to style an element based on its ancestors' CSS property value. Consider the HTML snippet below: <div><!-- ancestor --> <!-- Any number of descendant levels before reaching the target div --> < ...

Passing parameters between various components in a React application

Is it possible to pass a parameter or variable to a different component in React with react-router 3.0.0? For example, if a button is clicked and its onClick function redirects to another component where the variable should be instantly loaded to display a ...

list of key combinations in a ul element

I programmed a list of world states in PHP using an unordered list (<ul>): $WORLD_STATES = array( "France", "Germany", "Greece", "Greenland", "United Kingdom", "United States", "Uruguay" ...

What is the most effective method for organizing a React application with CRUD functionalities?

I apologize if the title is unclear, but when I refer to CRUD pages, I mean pages such as /posts for displaying all posts, /posts/create to show the post creation form, and /posts/:id/edit to display the form with data for updating. My current structure i ...