Search code examples
javascriptfor-loopdomaddeventlisteneronclicklistener

Why the method event listener click works only first time in for loop


i want to run the function "checkSlotsChoosen" every time i click on the buttons "slotsAvailables" and on "buttonBooking" but it works only for the first click, in every buttons. I add console.log to see how it works. Do you know why? Thank you in advance!

enter image description here


<script>
var buttonBooking = document.querySelector('.pbSubmit');
var slotsAvailables = document.querySelectorAll('.availableslot a');

buttonBooking.classList.add('disable');

function checkSlotsChoosen(){
        var slotsChoosen = document.querySelectorAll('.availableslot.currentSelection');
        var slotsCancel = document.querySelectorAll('.cancel');

        if(slotsChoosen.length === 0){
                buttonBooking.classList.add('disable');
                console.log('disable btn');
        }else{
                buttonBooking.classList.remove('disable');
                console.log('active btn');
        }

        for (let b = 0; b < slotsCancel.length; b++) {
            slotsCancel[b].addEventListener('click', checkSlotsChoosen);
        }
    }



for (let a = 0; a < slotsAvailables.length; a++) {
    slotsAvailables[a].addEventListener('click', checkSlotsChoosen);
}


buttonBooking.addEventListener('click', checkSlotsChoosen);
<script>


Solution

  • I think what you're trying to do is re-apply event listeners to the items in the list of selected times because you're dynamically adding them. This is probably the wrong way forward.

    As subparry mentions in their comment you can add one listener to a containing element that can catch events from its child elements as they "bubble up" the DOM - a process known as event delegation.

    In this example (I had to make up the HTML based on your image) I've got three sections - one for the time buttons, one for the list, and one for the form (represented by just the "Book" button here).

    For the time button section and the list section I've added one event listener to each.

    Whenever a time button is clicked its parent section element catches the event, checks to see if the element that was clicked was a time button, and its handler creates a new list element (the time button's text content, and a cancel button), and adds it to the list. It then checks if the book button is disabled and enables if it is.

    When a cancel button in a list item is clicked the event listener on the container is triggered, and its handler called. The handler checks to see if the element that was clicked was a cancel button, finds its closest list item element, and removes it. It also performs a check to see if the list has any children remaining and if it doesn't, disables the button.

    Summary: with event delegation there's no longer a need to add event listeners on dynamically created elements.

    // Cache the containers, and the book button
    const times = document.querySelector('.times');
    const list = document.querySelector('.list');
    const book = document.querySelector('.book');
    
    // Add two listeners to the times buttons
    // and the list sections
    times.addEventListener('click', handleTime);
    list.addEventListener('click', handleList);
    
    // When the handleTime handler is called it
    // checks that the clicked element has a "time"
    // class, creates a new list item, and adds it
    // to the list. If the "book" button is disabled
    // it enables it
    function handleTime(e) {
      if (e.target.matches('.time')) {
        const time = e.target.textContent;
        const item = `
          <li>
            ${time}
            <button
              type="button"
              class="cancel"
              data-time="${time}"
            >Cancel
            </button>
          </li>`;
        list.insertAdjacentHTML('beforeend', item);
        if (book.disabled) book.disabled = false;
      }
    }
    
    // When the `handleList` is called it checks to
    // see if the clicked element has a "cancel" class,
    // removes its closest list item (ie its parent element)
    // and disables the book button if there are no longer any
    // list items in the list
    function handleList(e) {
      if (e.target.matches('.cancel')) {
        e.target.closest('li').remove();
        if (list.children.length === 0) {
          book.disabled = true;
        }
      }
    }
    <section class="times">
      <button class="time">10:15</button>
      <button class="time">10:45</button>
      <button class="time">11:15</button>
      <button class="time">11:45</button>
      <button class="time">12:15</button>
    </section>
    <section>
      <ul class="list">
      </ul>
    </section>
    <section class="form">
      <button
        type="button"
        class="book"
        disabled
      >Book</button>
    </section>