0

I'm trying to loop through 2 simple arrays of elements and add 'click' event listeners to them. For some reason, each item the event listener is added to fires 3 times on click. The length of the arrays is correct, and the js module is only being served once.

The intended behavior is to fire the function once when a thumbnail or title is clicked.

lightbox.js

export const lightbox = () => {
  const thumbnails = document.querySelectorAll('.video-lightbox-gallery .thumbnail-wrapper');
  const titles = document.querySelectorAll('.video-lightbox-gallery .video-title');

  console.log(thumbnails.length) // 3
  console.log(titles.length) // 3

  setClickEvents(thumbnails)
  setClickEvents(titles)

  function setClickEvents(arr) {
    arr.forEach(item => {
      item.addEventListener('click', function() {
        console.log('event fired')
      })
    })
  }
}

main.js

document.addEventListener('DOMContentLoaded', () => {
  if (document.querySelector('.video-lightbox-gallery')) {
    import('./components/lightbox.js')
      .then(module => {
        module.lightbox()
      })
  }
})

video_lightbox_gallery.twig

<div class="video-lightbox-gallery">
  <div class="container">
    <div class="grid">
      {% for video in gallery.videos %}
        <div class="video-wrapper">
          <div class="thumbnail-wrapper">
            <div class="thumbnail" style="background-image: url('{{ Image(video.thumbnail).src }}');"></div>
            <svg class="play-btn" data-name="Group 414" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 76.378 76.378"><g data-name="Ellipse 1" fill="#fff" stroke="#fff" stroke-width="1.5"><circle cx="38.189" cy="38.189" r="38.189" stroke="none"/><circle cx="38.189" cy="38.189" r="37.439" fill="none"/></g><path data-name="Polygon 1" d="m52.098 38.19-19.18 11.107V27.082Z" fill="#538d9c"/></svg>
          </div>
          <span class="video-title">{{ video.title }}</span>
        </div>
        <div class="video-lightbox-template">
          <iframe src="{{ video.link }}?autoplay=1" title="{{ video.title }}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
        </div>
      {% endfor %}
    </div>
  </div>
</div>
Koshua
  • 47
  • 9
  • I don't see anything obvious. Is the number of times it fires equal to the length of the gallery? – Barmar Nov 05 '21 at 20:26
  • If you put a `console.log()` inside the `forEach` loop, how many times does that log? – Barmar Nov 05 '21 at 20:27
  • are the titles inside the thumbnails? – pilchard Nov 05 '21 at 21:57
  • @Barmar If I `console.log(thumbnail)`, it logs each of the 3 thumbnail elements once as expected. The unexpected behavior is happening when the event listener is added...it's almost like it's looping again while adding them to each element somehow. – Koshua Nov 08 '21 at 14:06
  • @pilchard The title elements are direct siblings of the thumbnail elements. They don't overlap and they have margin between. – Koshua Nov 08 '21 at 14:08
  • 1
    I can't think of any reason other than `lightbox()` being called multiple times. Set a breakpoint to see if this is happening. – Barmar Nov 08 '21 at 15:52
  • though I would recommend event delegation anyway instead of setting multiple dynamically added listeners. – pilchard Nov 08 '21 at 21:45
  • @pilchard Do you have an article or resource in mind that you could point me to? Would love to learn more. – Koshua Nov 08 '21 at 21:59
  • You can see this question [What is DOM Event delegation?](https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation) which has links to more – pilchard Nov 08 '21 at 22:01
  • 1
    @pilchard Thanks, that process makes more sense. – Koshua Nov 09 '21 at 16:53
  • So I figured out it's something to do with my Webpack setup. My dev process is firing that function 3 times, but when I build the files, it only fires once. – Koshua Nov 10 '21 at 14:20

0 Answers0