0

I have a tree view based on this https://www.w3schools.com/howto/howto_js_treeview.asp

It has several layers of nodes. Users have said they would like a button to expand the whole tree view (all the nodes, all the way down) in a single action.

How can I do this?

I'm afraid I know very little about Javascript (I'm a Python programmer and I'm generating all the HTML, CSS and JS from Python). In JS not sure if I can query for all nested nodes and then open them. Or if I should recurse in some way.

This is Javascript which opens a single node:

var toggler = document.getElementsByClassName("caret");
var i;

for (i = 0; i < toggler.length; i++) {
  toggler[i].addEventListener("click", function() {
    this.parentElement.querySelector(".nested").classList.toggle("active");
    this.classList.toggle("caret-down");
  });
} 
/* Remove default bullets */
ul, #myUL {
  list-style-type: none;
}

/* Remove margins and padding from the parent ul */
#myUL {
  margin: 0;
  padding: 0;
}

/* Style the caret/arrow */
.caret {
  cursor: pointer;
  user-select: none; /* Prevent text selection */
}

/* Create the caret/arrow with a unicode, and style it */
.caret::before {
  content: "\25B6";
  color: black;
  display: inline-block;
  margin-right: 6px;
}

/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.caret-down::before {
  transform: rotate(90deg);
}

/* Hide the nested list */
.nested {
  display: none;
}

/* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */
.active {
  display: block;
} 
<ul id="myUL">
  <li><span class="caret">Beverages</span>
    <ul class="nested">
      <li>Water</li>
      <li>Coffee</li>
      <li><span class="caret">Tea</span>
        <ul class="nested">
          <li>Black Tea</li>
          <li>White Tea</li>
          <li><span class="caret">Green Tea</span>
            <ul class="nested">
              <li>Sencha</li>
              <li>Gyokuro</li>
              <li>Matcha</li>
              <li>Pi Lo Chun</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul> 
CodeBug
  • 1,649
  • 1
  • 8
  • 23
quite68
  • 31
  • 7
  • Use `querySelectorAll` on the topmost `ul`, and then loop over the result, and set the `active` class for each element. – CBroe Mar 20 '23 at 08:32

2 Answers2

1

It's actually simpler than before, just need to use forEach function to toggle class.

Edit:

I add nest.every(d => !d.classList.contains('active')) to check if the tree was opened.

const toggler = [...document.getElementsByClassName("caret")];
const nest = [...document.querySelectorAll(".nested")]
const expandAll = document.getElementById("expand-all")
toggler.forEach(d => d.addEventListener('click', e => {
  e.target.parentElement.querySelector(".nested").classList.toggle("active");
  e.target.classList.toggle("caret-down");
}))

expandAll.addEventListener('click', () => {
  if (nest.every(d => !d.classList.contains('active'))) {
    toggler.forEach(t => t.classList.add('caret-down'))
    nest.forEach(n => n.classList.add('active'))
  } else {
    toggler.forEach(t => t.classList.remove('caret-down'))
    nest.forEach(n => n.classList.remove('active'))
  }
})
/* Remove default bullets */

ul,
#myUL {
  list-style-type: none;
}


/* Remove margins and padding from the parent ul */

#myUL {
  margin: 0;
  padding: 0;
}


/* Style the caret/arrow */

.caret {
  cursor: pointer;
  user-select: none;
  /* Prevent text selection */
}


/* Create the caret/arrow with a unicode, and style it */

.caret::before {
  content: "\25B6";
  color: black;
  display: inline-block;
  margin-right: 6px;
}


/* Rotate the caret/arrow icon when clicked on (using JavaScript) */

.caret-down::before {
  transform: rotate(90deg);
}


/* Hide the nested list */

.nested {
  display: none;
}


/* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */

.active {
  display: block;
}
<button id="expand-all">expand all</button>
<ul id="myUL">
  <li><span class="caret">Beverages</span>
    <ul class="nested">
      <li>Water</li>
      <li>Coffee</li>
      <li><span class="caret">Tea</span>
        <ul class="nested">
          <li>Black Tea</li>
          <li>White Tea</li>
          <li><span class="caret">Green Tea</span>
            <ul class="nested">
              <li>Sencha</li>
              <li>Gyokuro</li>
              <li>Matcha</li>
              <li>Pi Lo Chun</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>
Shuo
  • 1,512
  • 1
  • 3
  • 13
  • That looks pretty good. However I don't want to change the existing behaviour I want to add an extra button which does the 'expand all'. – quite68 Mar 20 '23 at 09:14
  • Looks good in the test code. I now need to try to apply it to my code. – quite68 Mar 20 '23 at 09:38
0

The simplest way would be to update the .nested class in the CSS, either remove it, replace it or change the styling

Something like

document.getElementsByClassName("nested").style.display="block";

Should do the trick, not the best solution but you should get the general idea.

You could also

  • add your active class to all .nested in a similar fashion
  • use a third class for targeting and remove all .nested on a toggle
  • use a CSS variable for the display value on .nested and update that (probably what I'd do)
  • use JQuery (like it's still 15 years ago) and $('.nested').show();

See this question for more details : How to change CSS property using JavaScript

Separatrix
  • 272
  • 2
  • 9