user1893874
user1893874

Reputation: 843

Collapse and Expand Tree structure in Javascript

I need a help on collapse and Expand using Javascript. Here is my running code (.html)

 <h2>Test</h2>
  <html lang="en">
  <head>
     <META http-equiv="Content-Type" content="text/html; charset=utf-16">
     <title></title>
     <script type="text/javascript">  
        function toggleDisplay(element) 
        {       
          element.style.display = element.style.display === 'none' ? '' : 'none'; 
        };
        function toggleDisplayAll(elements) 
        { 
          for(var i=0; i<elements.length; i++)
           {
              toggleDisplay(elements[i]);
           }
        }   
     </script>
   </head>
   <body>  
    <ul>
      <a onclick="toggleDisplayAll(this.parentNode.getElementsByTagName('ul')); return false;" href="#">Name:</a>
        <ul style="display:none;">
        <a onclick="toggleDisplayAll(this.parentNode.getElementsByTagName('ul')); return false;" href="#">Address: </a>
            <ul style="display:none;">
            <a onclick="toggleDisplayAll(this.parentNode.getElementsByTagName('li')); return false;" href="#">Subject: </a>
                <ul style="display:none;">
                    <li style="display:none;">Id
                    </li>
                </ul>
            </ul>
         </ul>
     </ul>
  </body>
 </html>

If you run this html, you will get out put as

 Name

on click of Name, it is showing all the child elements

 Name:
    Address:
        Subject:

On click of Subject it is showing Id

  Name:
    Address:
        Subject:
               . Id

What i want here is each child should open only on parent click.

When run the html, only Name will dispaly

  Name:

On click of Name, only Address will be displayed as a child element.

   Name:
        Address:

Onclick of Address, only Subject will display

   Name:
        Address:
            Subject:

Than finally on click of Subject, id will show up

   Name:
        Address:
            Subject:
                 . Id

How to implement this tree structure. what i am doing wrong here. please suggest me.

Upvotes: 6

Views: 23088

Answers (3)

w3debugger
w3debugger

Reputation: 2102

In pure JavaScript

document.querySelector('.tree-structure').addEventListener('click', (e) => {
  const el = e.target;
  const sibling = el.nextSibling.nextSibling;

  if (el && el.className == 'toggle' && sibling) {
    sibling.classList.toggle('show');
  }
});
ul ul {
  display: none;
}

.show {
  display: block;
}
<ul class="tree-structure">
  <li>
    <button class="toggle">1st Level</button>

    <ul>
      <li>
        <button class="toggle">2nd Level</button>

        <ul>
          <li>
            <button class="toggle">3rd Level</button>

            <ul>
              <li>
                <button class="toggle">4th Level</button>
              </li>
              <li>
                <button class="toggle">4th Level</button>
              </li>
            </ul>
          </li>
          <li>
            <button class="toggle">3rd Level</button>

            <ul>
              <li>
                <button class="toggle">4th Level</button>
              </li>
              <li>
                <button class="toggle">4th Level</button>
              </li>
            </ul>
          </li>
        </ul>
      </li>
      <li>
        <button class="toggle">2nd Level</button>

        <ul>
          <li>
            <button class="toggle">3rd Level</button>

            <ul>
              <li>
                <button class="toggle">4th Level</button>
              </li>
              <li>
                <button class="toggle">4th Level</button>
              </li>
            </ul>
          </li>
          <li>
            <button class="toggle">3rd Level</button>

            <ul>
              <li>
                <button class="toggle">4th Level</button>
              </li>
              <li>
                <button class="toggle">4th Level</button>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <button class="toggle">1st Level</button>

    <ul>
      <li>
        <button class="toggle">2nd Level</button>

        <ul>
          <li>
            <button class="toggle">3rd Level</button>

            <ul>
              <li>
                <button class="toggle">4th Level</button>
              </li>
              <li>
                <button class="toggle">4th Level</button>
              </li>
            </ul>
          </li>
          <li>
            <button class="toggle">3rd Level</button>

            <ul>
              <li>
                <button class="toggle">4th Level</button>
              </li>
              <li>
                <button class="toggle">4th Level</button>
              </li>
            </ul>
          </li>
        </ul>
      </li>
      <li>
        <button class="toggle">2nd Level</button>

        <ul>
          <li>
            <button class="toggle">3rd Level</button>

            <ul>
              <li>
                <button class="toggle">4th Level</button>
              </li>
              <li>
                <button class="toggle">4th Level</button>
              </li>
            </ul>
          </li>
          <li>
            <button class="toggle">3rd Level</button>

            <ul>
              <li>
                <button class="toggle">4th Level</button>
              </li>
              <li>
                <button class="toggle">4th Level</button>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

Upvotes: 1

Oka
Oka

Reputation: 26355

You want to target just the first child element, instead of looping through all of them. You should also try to separate your logic from your markup. Give unobtrusive JavaScript a read.

function toggle() {
  var ls = this.parentNode.getElementsByTagName('ul')[0],
      styles, display;

  if (ls) {
    styles = window.getComputedStyle(ls);
    display = styles.getPropertyValue('display');

    ls.style.display = (display === 'none' ? 'block' : 'none');
  }
}


var eles = document.querySelectorAll('.ele');

Array.prototype.slice.call(eles).forEach(function (e) {
  e.addEventListener('click', toggle);
});
ul ul {
  display: none;
}

.ele {
  cursor: pointer;
}

.ele:hover {
  color: red;
}
<ul>
  <li><span class="ele">One</span>
    <ul>
      <li><span class="ele">Two</span>
        <ul>
          <li><span class="ele">Three</span>
            <ul>
              <li><span class="ele">Four</span></li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

Upvotes: 2

Nanang Mahdaen El-Agung
Nanang Mahdaen El-Agung

Reputation: 1434

Check this:

$('.expand').click(function() {
  $('ul', $(this).parent()).eq(0).toggle();
});
ul li ul {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
  <li>
    <a class="expand">Root</a>
    <ul>
      <li>
        <a class="expand">Child</a>
        <ul>
          <li>
            <a class="expand">Super Child</a>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

Edit

If you don't want to use jQuery, you can try this:

var expander = document.querySelectorAll('.expand');

for (var i = 0; i < expander.length; ++i) {
  expander[i].onclick = function() {
    var ul = this.parentElement.querySelectorAll('ul')[0];
    
    if (ul.offsetHeight > 0) {
      ul.style.display = 'none';
    } else {
      ul.style.display = 'block';
    }
  }
}
ul li ul {
  display: none;
}
    <ul>
      <li>
        <a class="expand">Root</a>
        <ul>
          <li>
            <a class="expand">Child</a>
            <ul>
              <li>
                <a class="expand">Super Child</a>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>

Upvotes: 12

Related Questions