2

I am having trouble correctly toggling classes in my tab list using javascript.

At first click the menu is open and on second click on the same will close, but after close will not open again if I click on the same menu.

Check my example to see exactly what I mean:

var prev = '';
var tabsList = document.querySelectorAll('.main-col');

for (i = 0; i < tabsList.length; ++i) {

  tabsList[i].addEventListener("click", function(){
  var elements = document.getElementsByClassName('main-col');
  for (j = 0; j < elements.length; ++j) {
    elements[j].classList.remove("open");
  }

  var index = this.dataset.foo;

  document.querySelectorAll('[data-foo="' + index + '"]')[0].classList.toggle("open");

  if ( prev == this) {
    this.classList.remove("open");
  }

  prev = this;

  });

}
h3 {
  cursor: pointer;
}
 
.main-col .menu {
  max-height: 0;
  overflow: hidden;
  transition: max-height .5s cubic-bezier(0, 1, 0, 1);
}

.main-col.open .menu {
  background: #333;
  max-height: 500px;
  transition: max-height .5s ease-in-out;
}
<div id="f-col1" class="main-col" data-foo="1">
    <h3 class="title-col1">Column 1 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-1">
        <ul id="column-1-container" class="menu">
            <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
            <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
            <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
        </ul>
    </div>
</div>
<div id="f-col2" class="main-col" data-foo="2">
<h3 class="title-col2">Column 2 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-2">
        <ul id="column-2-container" class="menu">
            <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
            <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
            <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
        </ul>
    </div>
</div>
<div id="f-col3" class="main-col" data-foo="3">
<h3 class="title-col3">Column 3 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-3">
        <ul id="column-3-container" class="menu">
            <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
            <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
            <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
        </ul>
    </div>
</div>

PS: Please don't give me alternatives with jQuery to this little problem. :)

3 Answers 3

2

You could achieve this by the following:

// Iterate each tab (main-col) that the toggle behaviour is applied to
document.querySelectorAll('.main-col').forEach(function(tabItem) {
  
  // Add click event listener to current tab to process toggle logic
  tabItem.addEventListener("click", function() {
    
    // Use the toggle method to toggle open class on and off
    tabItem.classList.toggle('open');
    
    // Select any currently open tabs, and remove the open class from tabs
    // that are not the tab we're currently toggling
    document.querySelectorAll('.main-col.open').forEach(function(openTabItem) {
      
      if(openTabItem !== tabItem) {
        openTabItem.classList.remove('open');      
      }
    });
  })
})
h3 {
  cursor: pointer;
}
 
.main-col .menu {
  max-height: 0;
  overflow: hidden;
  transition: max-height .5s cubic-bezier(0, 1, 0, 1);
}

.main-col.open .menu {
  background: #333;
  max-height: 500px;
  transition: max-height .5s ease-in-out;
}
<div id="f-col1" class="main-col" data-foo="1">
    <h3 class="title-col1">Column 1 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-1">
        <ul id="column-1-container" class="menu">
            <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
            <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
            <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
        </ul>
    </div>
</div>


<div id="f-col2" class="main-col" data-foo="2">
<h3 class="title-col2">Column 2 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-2">
        <ul id="column-2-container" class="menu">
            <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
            <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
            <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
        </ul>
    </div>
</div>


<div id="f-col3" class="main-col" data-foo="3">
<h3 class="title-col3">Column 3 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-3">
        <ul id="column-3-container" class="menu">
            <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
            <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
            <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
        </ul>
    </div>
</div>

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for pointing out the part that I have overlooked in my answer.
@DacreDenny, works perfect on modern browser, but can you modify it to work also on IE11. IE11 don't understand "forEach". I need it to work on IE11, thanks!
1

IE11 solution, with no polyfill (forEach)...

let FcolsParent = document.getElementById("Parent_of_f-cols");

FcolsParent.onclick = function(e) {
    if (!e.target.matches('h3')) return;
    e.stopPropagation();

    let
      isOpen    = e.target.parentNode.classList.contains('open'),
      Open_Tabs = FcolsParent.querySelectorAll('.main-col.open')
    ;
    for( let i=0, iMax=Open_Tabs.length; i<iMax; i++ ) {
      Open_Tabs[i].classList.remove('open');
    }
    if (!isOpen) e.target.parentNode.classList.add('open');
}
h3 {
  cursor: pointer;
}
.main-col .menu {
  max-height: 0;
  overflow: hidden;
transition: max-height 1.5s cubic-bezier(0, 1, 0, 1);
  /*   transition: max-height 1.5s ease-in-out; */
}

.main-col.open .menu {
  background: #333;
  max-height: 500px;
  transition: max-height 1.5s ease-in-out;
}
<div id="Parent_of_f-cols">
  <div id="f-col1" class="main-col" data-foo="1">
    <h3 class="title-col1">Column 1 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-1">
      <ul id="column-1-container" class="menu">
        <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
        <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
        <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
      </ul>
    </div>
  </div>
  <div id="f-col2" class="main-col" data-foo="2">
    <h3 class="title-col2">Column 2 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-2">
      <ul id="column-2-container" class="menu">
        <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
        <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
        <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
      </ul>
    </div>
  </div>
  <div id="f-col3" class="main-col" data-foo="3">
    <h3 class="title-col3">Column 3 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-3">
      <ul id="column-3-container" class="menu">
        <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
        <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
        <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
      </ul>
    </div>
  </div>
</div>

Comments

1

what about if a click will happen on one of your link ? actually this remove too your ".open" class

    let FcolsParent = document.getElementById("Parent_of_f-cols");

    FcolsParent.onclick = function(e) {
        if (!e.target.matches('h3')) return;
        e.stopPropagation();

        let isOpen = e.target.parentNode.classList.contains('open');
        FcolsParent.querySelectorAll('.main-col.open').forEach( openTabItem=>openTabItem.classList.remove('open') ); 
        if (!isOpen) e.target.parentNode.classList.add('open');
    }
h3 {
  cursor: pointer;
}
.main-col .menu {
  max-height: 0;
  overflow: hidden;
  transition: max-height 1.5s cubic-bezier(0, 1, 0, 1);
}
.main-col.open .menu {
  background: #333;
  max-height: 500px;
  transition: max-height 1.5s ease-in-out;
}
<div id="Parent_of_f-cols">
  <div id="f-col1" class="main-col" data-foo="1">
    <h3 class="title-col1">Column 1 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-1">
      <ul id="column-1-container" class="menu">
        <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
        <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
        <li id="menu-item-1" class="menu-item"><a href="#">LINK 1</a></li>
      </ul>
    </div>
  </div>
  <div id="f-col2" class="main-col" data-foo="2">
    <h3 class="title-col2">Column 2 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-2">
      <ul id="column-2-container" class="menu">
        <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
        <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
        <li id="menu-item-2" class="menu-item"><a href="#">LINK 2</a></li>
      </ul>
    </div>
  </div>
  <div id="f-col3" class="main-col" data-foo="3">
    <h3 class="title-col3">Column 3 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
    <div class="column-3">
      <ul id="column-3-container" class="menu">
        <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
        <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
        <li id="menu-item-3" class="menu-item"><a href="#">LINK 3</a></li>
      </ul>
    </div>
  </div>
</div>

2 Comments

MrJ can you make it work on IE11 please. IE11 don't understand "forEach" :(
the best for IE11 is to use a polyfill, the systems go out of fashion but the miles of codes exist for a long time. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.