Как сделать так, чтобы мой аккордеон/складной работал с делегированием событий?

#javascript #dom #accordion #event-delegation

Вопрос:

Я хочу создать аккордеон в ящике корзины моего интернет-магазина, но я не могу найти способ решить свою проблему.

Что здесь происходит, так это то, что я получаю элемент, но как только ящик повторно отображается, моя ссылка теряется. Поскольку DOM динамически меняется, я не должен делать это таким образом. Вместо этого я должен использовать делегирование событий, чтобы мой код мог работать даже при повторной визуализации.

 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:

Есть элемент контейнера аккордеона, к которому вы прикрепляете прослушиватель. Затем также кэшируйте разделители внутри каждой панели div. Используйте классы, чтобы указать, должна ли информация на каждой панели быть видимой. Изначально мы настроили их всех на 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> 

Комментарии:

1. Спасибо, что посмотрели на это! Он все еще не работает после замены моего кода на ваш. Я добавил тип=»кнопка» к кнопкам, потому что он перенаправлял на страницу корзины, когда я нажал на нее. После добавления типа=»кнопка» он ничего не делает при нажатии на текст. Может ли быть проблемой то, что он находится в форме?

2. @Морис, я обновил свой ответ, так что, надеюсь, он лучше ответит на ваш вопрос. Если вы добавляете элементы, вам нужно специально выбрать все элементы в обработчике. Просто настройте свой код в соответствии с вашей разметкой. Я упростил его для этого примера, но надеюсь, вы понимаете, что я сделал.