简体   繁体   中英

Adding remove class section to existing eventlistener loop

What I am trying to do, is loop through all my.nav__btn elements, use them as buttons through the addEventListener, and through that add the class.open to certain elements.

This part actually works fine, and I even managed to make it remove the.open class from other elements, when a new one is triggered.

However, I have tried in multiple ways now to make it remove the.open class, if the same button is pressed again.

I have tried using:

if (sections[x].classList.contains('open')) { 
    sections.classList.remove('open')
}

in different locations of the function, but without success. Either it breaks the script or simply has no effect.

my only solution is creating a completely seperate function to handle this matter, and have 2 EventListeners on the same elements, but I think it can be worked into this script. I just do not have the skills to do so, aparently.

Below a complete snippet:

 var btns = document.querySelectorAll('.nav__btn'); var sections = document.querySelectorAll('section'); for (var i = 0; i < btns.length; i++) { (function(x) { btns[x].addEventListener('click', _ => { if (sections[x].classList.contains('open')) { sections[x].classList.remove('open') } sections.forEach(section => { if (section.classList.contains('open')) { section.classList.remove('open') } }) sections[x].classList.toggle('open') }) })(i) }
 section { position: absolute; top: 0; right: 0; height: 50vh; width: 50vw; opacity: 0; visiblity: 0; z-index: 30; } section:nth-child(2) { background: #00ff00; } section:nth-child(3) { background: #ff0000; } section:nth-child(4) { background: #0000ff; } section:nth-child(5) { background: #ccc; }.open { opacity: 1; visibility: 1; }
 <nav class="nav"> <ul class="nav__list"> <li><span class="nav__btn">Button 1</span></li> <li><span class="nav__btn">Button 2</span></li> <li><span class="nav__btn">Button 3</span></li> <li><span class="nav__btn">Button 4</span></li> </ul> </nav> <section>section 1</section> <section>section 2</section> <section>section 3</section> <section>section 4</section>

Additionaly, here is a JsFiddle link: https://jsfiddle.net/ajLxbqzk/4/

To handle correctly sections display, you can loop through them this way:

  • If a section is associated to the button, toggle its display
  • Otherwise, hide it

Below a working version:

var btns = document.querySelectorAll('.nav__btn');
var sections = document.querySelectorAll('section');

// We loop through all buttons in the btns collection
// "i" is the index of the current button processed in the loop
btns.forEach((btn, i) => {
  btn.addEventListener('click', _ => {
    // If btns[i] is clicked, loop through sections...
    sections.forEach((section, j) => {
      if (i == j) {
        // The current section is of index i (because i == j)
        // We toogle it
        section.classList.toggle('open');
      } else {
        // Other sections are just hided
        section.classList.remove('open');
      }
    });
  });
});

This is your click handler code:

  //1
  if (sections[x].classList.contains('open')) {
    sections[x].classList.remove('open')
  }

  //2
  sections.forEach(section => {
    if (section.classList.contains('open')) {
      section.classList.remove('open')
    }
  })

  //3
  sections[x].classList.toggle('open')

I labeled it into three steps. When you click a button, these three things happen:

  1. If the current section was open, close it.
  2. Close all open sections.
  3. Toggle the current section.

Step 1 is redundant because step 2 closes all sections anyway - the end result will always be all sections being closed.

Since step 2 ensures that all sections are always closed by the time you reach step 3, step 3 only can possibly do one thing: set the current section to open. The current section can never be open at this point - because it was just closed in both step 1 and 2 - so sections[x].classList.toggle('open') can't possibly do anything other than make the section open.

What you've described as what you want to happen is not the function that you wrote. What you want is something like this:

  1. Close all other sections.
  2. Toggle the current section.

Because that way, when step 2 runs, the current section might still be open, therefore toggling it sets it to closed. So all you have to do is write it exactly as described:

  //1 - close all sections EXCEPT sections[x]
  sections.forEach(section => {
    if (section != sections[x] && section.classList.contains('open')) {
      section.classList.remove('open')
    }
  })

  //2 - toggle sections[x]
  sections[x].classList.toggle('open')

Here it is in your complete example:

 var btns = document.querySelectorAll('.nav__btn'); var sections = document.querySelectorAll('section'); for (var i = 0; i < btns.length; i++) { (function(x) { btns[x].addEventListener('click', _ => { //1 - close all sections EXCEPT sections[x] sections.forEach(section => { if (section.= sections[x] && section.classList.contains('open')) { section.classList.remove('open') } }) //2 - toggle sections[x] sections[x].classList.toggle('open') }) })(i) }
 section { position: absolute; top: 0; right: 0; height: 50vh; width: 50vw; opacity: 0; visiblity: 0; z-index: 30; } section:nth-child(2) { background: #00ff00; } section:nth-child(3) { background: #ff0000; } section:nth-child(4) { background: #0000ff; } section:nth-child(5) { background: #ccc; }.open { opacity: 1; visibility: 1; }
 <nav class="nav"> <ul class="nav__list"> <li><span class="nav__btn">Button 1</span></li> <li><span class="nav__btn">Button 2</span></li> <li><span class="nav__btn">Button 3</span></li> <li><span class="nav__btn">Button 4</span></li> </ul> </nav> <section>section 1</section> <section>section 2</section> <section>section 3</section> <section>section 4</section>

I really don't like using loops in code. It creates messy code and looping over elements takes time. I prefer storing variables instead.

I removed your inner method that had x as an argument. Instead, I changed var i in your loop to let i , otherwise i would have the value of 4 inside the anonymous click method.

I created a content element as a container for all the sections which I which added "content" as an id.

I then just add the attribute previous-index to content and let that keep track of which section that was previously opened and let an if statement decide if I should remove the open class or not. I could use a global variable outside of the click methods but using global variables are always in the risk of being overwritten in larger projects.

I'm using sectionIsOpen and newButtonClicked in order to explain code through variables, because it's harder to read "not statements", like i != previouslyClickedIndex .

Finally, you're using the wrong values on visibility . It should be hidden or visible , not 0 or 1.

 const btns = document.querySelectorAll('.nav__btn'); const sections = document.querySelectorAll('section'); const content = document.getElementById('content'); for (let i = 0; i < btns.length; i++) { btns[i].addEventListener('click', () => { const NOT_SET = -1; let previouslyClickedIndex = content.getAttribute('previous-index') || NOT_SET; let sectionIsOpen = previouslyClickedIndex;= NOT_SET; let newButtonClicked = i.= previouslyClickedIndex. if (sectionIsOpen && newButtonClicked) { sections[previouslyClickedIndex];classList.remove('open'). } sections[i];classList.toggle('open'), content;setAttribute('previous-index'; i); }); }
 section { position: absolute; top: 0; right: 0; height: 50vh; width: 50vw; opacity: 0; visiblity: hidden; z-index: 30; } section:nth-child(2) { background: #00ff00; } section:nth-child(3) { background: #ff0000; } section:nth-child(4) { background: #0000ff; } section:nth-child(5) { background: #ccc; }.open { opacity: 1; visibility: visible; }
 <nav class="nav"> <ul class="nav__list"> <li><span class="nav__btn">Button 1</span></li> <li><span class="nav__btn">Button 2</span></li> <li><span class="nav__btn">Button 3</span></li> <li><span class="nav__btn">Button 4</span></li> </ul> </nav> <content id="content"> <section>section 1</section> <section>section 2</section> <section>section 3</section> <section>section 4</section> </content>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM