How to close template modals in handlebars?

Viewed 59

I created an app that receives articles from news API. Each article is displayed in a card, which has a button "Open Modal". This button opens a modal with the unique information that pertains to each respective article.

However, I am unable to close the modal once it's opened. I suspect it's because the modal is stuck in this state: modals.forEach((modal, index) => {modal.classList.toggle('open', index === openIndex);

Here is my current code:

 {{!-- #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 data-open-modal="{{@index}}">Open Modal</button>
                        
            </div>
        </div>
        {{/each}}
    </div>
</div>


{{#each articles}}
 
    <!-- The Modal -->
    <div class="modal closed" id="Modal_{{@index}}">
        <!-- Modal content -->
        <div class="modal-content">
            <span id="spm" class="close" >&times;</span>
            <h2>{{title}}</h2>
            <img src="{{urlToImage}}" alt="">
            <p>{{content}}</p>
        </div>
    </div>

{{/each}}


<script>

    //Store all modals and modal buttons in variables
    const openModalButtons = document.querySelectorAll('[data-open-modal]');
    const modals = document.querySelectorAll('.modal');

    //Loop through all modal buttons and assign handler to each
    openModalButtons.forEach(openModalButton => {
        openModalButton.addEventListener('click', (event) => {
            //Get index value from number clicked
            const openIndex = Number(event.target.dataset.openModal); //Access dataset attribute to read and write
            
    //Loop over each modal. 
    //Set modal class to open if index is equal to wanted index
    modals.forEach((modal, index) => {
        modal.classList.toggle('open', index === openIndex);
        modal.classList.toggle('closed', index !== openIndex);
    });
  });
});

</script>

And here is what I tried adding to my script: (It gave no error but did nothing)

const span = document.querySelectorAll('.close');

let spanArr = Array.prototype.slice.call(span);
    
    spanArr.forEach(spanArr => {
        spanArr.addEventListener('click', (event) => {
            const closeIndex = Number(event.target.dataset.closeModal); 

            spanArr[closeIndex].forEach(span => {
                span.onclick = function() {
                    modal.style.display = "none";
                }
            });  
        });
    });

I also tried adding event listeners to the spans, but I was unable to make it work. I am a beginner and this is my first time using handlebars, so thank you for any insight!

1 Answers

There are some issues here that perhaps are due to absent explanations on my part in https://stackoverflow.com/a/73738690/3397771.

First, the reason that const openIndex = Number(event.target.dataset.openModal); works is because there is a data-attribute called data-open-modal defined on each "open" button. It is that data-attribute that we are referencing with dataset.openModal and the value we will get back is the value on the right-hand-side of the equal sign in the attribute, the {{@index}} part.

However, the data-attribute approach is probably excessively complicated for our purposes here. We could, alternatively, use the index obtained in the forEach loop we use to iteratively add our click event listeners.

Next, there is no need for the spanArr[closeIndex].forEach(... loop inside our click handler. spanArr - despite its name - is not an arr(ay); it is a single span element.

The updated code becomes:

const span = document.querySelectorAll('.close');

span.forEach((spanArr, index) => { 
  spanArr.addEventListener('click', () => {
    modals[index].style.display = "none";
  });
});

Note: I have left the names of the variables as I found them, but they could and should be improved. For example, span does not communicate what purpose of these elements is or, for that matter, that it is a collection. closeButtons would be a better name. In fact, elements that behave like buttons should use the <button> element, not <span>, so as to be semantically correct and accessible.

I have created a new fiddle.

Related