Moonfly
Moonfly

Reputation: 11

How to select specific elements with JS that are dynamically created?

<div class="gallery-container">
   <?php while (have_rows('gallery')): ?>
   [...]
      <div class="toggle-container">
         <button class="toggle-button active" onclick="gridView()">Grid</button>
         <button class="toggle-button" onclick="listView()">List</button> 
      </div>
      <div class="gallery-items grid-items">
          [...Gallery Items...]
      </div>
   <?php endwhile; ?>
</div>

What would be the best way to select specific elements on a page when the elements are created with a while loop shown above. It's an ever-growing list and elements can also be removed.

In this example I am generating a page full of small galleries together with the toggle buttons for the Grid/List view next to each gallery.

I am trying to make all of those buttons work with just the gallery they are generated together with.

I know how to select them based on their index manually, but I don't know how I could tweak the code to be able to make it work with every small gallery separately.

This is what I came up with to make it work with the first gallery:

<script>
    const button = document.getElementsByClassName('toggle-button');
    const element = document.getElementsByClassName('gallery-items');

function listView() {
    if ( element[0].classList.contains('grid-items') ){
        element[0].classList.remove("grid-items");
    }

    button[0].classList.toggle('active');
    button[1].classList.toggle('active');
}

function gridView() {
    if ( !element[0].classList.contains('grid-items') ){
        element[0].classList.add("grid-items");
    }

    button[0].classList.toggle('active');
    button[1].classList.toggle('active');
}
</script>

Upvotes: 0

Views: 68

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 371049

You might consider using event delegation instead: add a click listener to .gallery-container. If the clicked target is a .toggle-button, run the appropriate logic, selecting the relevant surrounding elements on click:

document.querySelector('.gallery-container').addEventListener('click', ({ target }) => {
  if (!target.matches('.toggle-button')) {
    return;
  }
  const toggleContainer = target.parentElement;
  const btns = toggleContainer.children;
  if (target === btns[0]) {
    btns[0].classList.add('active');
    btns[1].classList.remove('active');
  } else {
    btns[0].classList.remove('active');
    btns[1].classList.add('active');
  }
  const galleryItems = toggleContainer.nextElementSibling;
  if (target === btns[0]) {
    galleryItems.classList.add('grid-items');
  } else {
    galleryItems.classList.remove('grid-items');
  }
});
.active {
  background-color: yellow;
}
.grid-items {
  background-color: red;
}
<div class="gallery-container">
  <div class="toggle-container">
    <button class="toggle-button active">Grid</button>
    <button class="toggle-button">List</button>
  </div>
  <div class="gallery-items grid-items">
    [...Gallery Items...]
  </div>
  
    <div class="toggle-container">
    <button class="toggle-button active">Grid</button>
    <button class="toggle-button">List</button>
  </div>
  <div class="gallery-items grid-items">
    [...Gallery Items 2...]
  </div>
</div>

Note that there's no need to explicitly test if a classList.contains a particular class before adding it (though, there's no harm in doing so, it's just unnecessary).

Upvotes: 1

Related Questions