How to addeventlistener to modal buttons in handlebars template

Viewed 32

I am creating a web app that connects to a news API and shows articles. For each article, I have created a card and a modal in handlebars.

Upon clicking the button on each respective card, I would like a modal to open with its unique information.

I am trying to add an event listener for the button on the card to open the modal.

<div class="">

  
    {{!-- #each article --}}

    <div class="row">

        {{#each articles}}
        
        <div class="col-12-sm col-6-md col-3-lg">
            <div class="card m-2">
                <div class="card-body">
                <h5 class="card-title">{{title}}</h5>
                <p class="card-text">{{description}}</p>
                </div>
                <img class="card-image" src="{{urlToImage}}" alt="Card image cap">
                <button id="mybtn" class="{{@index}}">Open Modal</button>
                {{@index}}
                            
            </div>
        </div>

        {{/each}}

    </div>
</div>


{{#each articles}}

        <!-- The Modal -->
        <div id="modid" class="modal">

            <!-- Modal content -->
            <div class="modal-content">
                <span class="close">&times;</span>
                <p>Some text in the Modal..</p>
            </div>
        </div>

{{/each}}


<script>

    
    let modal = document.getElementById("modid");

    let btn = document.getElementById("mybtn");

    let span = document.getElementsByClassName("close")[0];

    btn.onclick = function() {
        modal.style.display = "block";
    }

    span.onclick = function() {
            modal.style.display = "none";
    }

    window.onclick = function(event) {
        if (event.target == modal) {
                modal.style.display = "none";
        }
    }

    const btn = document.querySelector('.{{@index}}');
    btn.addEventListener('click', function(event){
        console.log('Button Clicked');
    }

        
</script>

Clicking on the "Open Modal" button does nothing, nor does it give an error.

I am unsure if the button class {{@index}} is being read properly by the script.

Any assistance or tips would be greatly appreciated!

1 Answers

First, I think a better approach than using class="{{@index}} on each button would be to use a data-attribute, like data-open-modal="{{@index}}". This will allow us to use a query selector to get all of the open modal buttons as well as allow us to get the specific index in our click handler.

Next, we cannot use a single id for each of our modal elements as you are doing with id="modid". This leads to multiple elements with the same id, "modid", which is invalid HTML. Also, it prevents us from specifying which modal we want to open. Let's just remove that id.

In our JavaScript, we can select all of the open modal buttons and modals and store them in variables:

const openModalButtons = document.querySelectorAll('[data-open-modal]');
const modals = document.querySelectorAll('.modal');

Next, we can loop through all of openModalButtons and assign a click handler to each.

openModalButtons.forEach(openModalButton => {
  openModalButton.addEventListener('click', (event) => {
    const openIndex = Number(event.target.dataset.openModal);
    
    modals.forEach((modal, index) => {
      modal.classList.toggle('open', index === openIndex);
      modal.classList.toggle('closed', index !== openIndex)
    });
  });
});

Notice that our click handler gets the data-open-modal index value from the button that was clicked. This is the index of the modal to show.

Next, it loops over each modal element and sets the "open" class if the index is equal to the index we want to show; otherwise, it sets the "closed" class.

I have created a fiddle for reference.

Update: To handle "close" button clicks:

The close button is very similar to the open button. You would need to attach an event listener and you would need a way to know which modal element to target. We could use a data-attribute again, like <button class="close" data-close-modal="{{@index}}">&times;</button>. Note: To be semantically correct, a close button should be a <button element, not a <span>.

Our click event listener for the close buttons would be very similar to that of the open buttons. This time, however, we won't loop through all of the modals; we will just update the modal whose index in the array of modal elements is equal to the data-close-modal attribute value of the clicked close button.

const closeModalButtons = document.querySelectorAll('[data-close-modal]');

closeModalButtons.forEach(closeModalButton => {
  closeModalButton.addEventListener('click', (event) => {
    const closeIndex = Number(event.target.dataset.closeModal);
    
    modals[closeIndex].classList.remove('open');
    modals[closeIndex].classList.add('closed');
  });
});

Note: You may very well not require an .open and a .closed class for you modals - one may be sufficient. This is just an example.

An updated fiddle has been creeated.

Related