How do I make my accordion/collapsible work with event delegation?

I want to create an accordion in my webshop's cart drawer but I can't find a way to fix my issue.

What is happening here is that I get the element but as soon as the drawer is re-rendered then my reference is lost. Because the DOM dynamically changes, I should not do it this way. Instead, I should use event delegation so that my code can still work even when things are re-rendered.

var acc = document.getElementsByClassName("accordion");
var i;

for (i = 0; i < acc.length; i++) {
  acc[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var panel = this.nextElementSibling;
    if (panel.style.display === "block") {
      panel.style.display = "none";
    } else {
      panel.style.display = "block";
    }
  });
}
<button type="button" class="accordion">Expand me</button>
<div class="panel">
  <p>Text here...</p>
</div>

1 answer

  • answered 2021-09-23 13:00 Andy

    Have an accordion container element that you attach the listener to. Then also cache the divs within each panel div. Use classes to specify whether each panel's information should be visible. Initially we set them all to display: none.

    const accordion = document.querySelector('.accordion');
    const add = document.querySelector('.add');
    
    accordion.addEventListener('click', handleClick, false);
    add.addEventListener('click', handleAdd, false);
    
    function handleAdd() {
      const html = '<div class="panel"><button>Expand me: newPanel</button><div><p>New Panel</p></div></div>';
      accordion.insertAdjacentHTML('beforeend', html);
    }
    
    function handleClick(e) {
      const button = e.target;
    
      // Check to see if the element we clicked on was
      // the button
      if (button.nodeName === 'BUTTON') {
    
        // Find the closest panel ancestor
        const parent = button.closest('.panel');
    
        // Get all of the panels, even the ones newly added
        const panels = accordion.querySelectorAll('.panel div');
    
        // Remove all the `show` classes from the panels
        panels.forEach(panel => panel.classList.remove('show'));
    
        // And add `show` to the panels `div` which we previous hid
        parent.querySelector('div').classList.add('show');
      }
    }
    .panel div { display: none; }
    .panel div.show { display: block; }
    .add { margin-top: 1em; background-color: #44aa77; }
    <div class="accordion">
      <div class="panel">
        <button>Expand me: one</button>
        <div><p>One</p></div>
      </div>
      <div class="panel">
        <button>Expand me: two</button>
        <div><p>Two</p></div>
      </div>
      <div class="panel">
        <button>Expand me: three</button>
        <div><p>Three</p></div>
       </div>
    </div>
    <button class="add">Add new panel</button>

How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum