Bart Silverstrim
Bart Silverstrim

Reputation: 3625

How do I apply Javascript functions to multiple instances of a class?

I have a web page that is displaying a set of images using a table for simple layout. What I'd like to do is allow a user to click the image and have that image appear magnified in a modal.

The html row holding a graphic:

<tr>
    <td>Graphic One</td>
    <td><img class="graph" src="/chart1.jpg">
        <div class="modal">
        <div class="modal-content">
            <span class="close-btn">&times;</span>
                <img src="/chart1.jpg">
        </div>
    </div>
    </td>
</tr>

This is repeated to create more rows with graphics. Here's the script:

<script>
let modalBtn = document.querySelector(".graph")
let modal = document.querySelector(".modal")
let closeBtn = document.querySelector(".close-btn")
    modalBtn.onclick = function(){
    modal.style.display = "block"
    }
closeBtn.onclick = function(){
    modal.style.display = "none"
    }
    window.onclick = function(e){
    if(e.target == modal){
        modal.style.display = "none"
    }
}
</script>

This only seems to activate the first graphic on the page, though, so I'm assuming that the querySelector() stops at the first instance found.

How can I apply this to every row with an image in the table?

Upvotes: 0

Views: 51

Answers (1)

Terry
Terry

Reputation: 66113

If you want to apply it to every table row, all you need is to select all the <td> and then iteratively apply the same logic as you have above to the elements found inside it.

To access the individual elements, you can simply use Element.querySelector() instead, assuming Element refers to the <td>. It helps to give this <td> element that contains all these elements a class, so you can narrow down the selection, e.g. <td class="action">:

<tr>
    <td>Graphic One</td>
    <td class="action">
        <img class="graph" src="/chart1.jpg">
        <div class="modal">
        <div class="modal-content">
            <span class="close-btn">&times;</span>
                <img src="/chart1.jpg">
        </div>
    </div>
    </td>
</tr>

...then you can do this:

document.querySelectorAll('.action').forEach(el => {
    let modalBtn = el.querySelector(".graph");
    let modal = el.querySelector(".modal");
    let closeBtn = el.querySelector(".close-btn");

    modalBtn.addEventListener('click', e => {
        // Stop click event from bubbling up
        e.stopPropagation();

        modal.style.display = 'block';
    });

    closeBtn.addEventListener('click', e => {
        // Stop click event from bubbling up
        e.stopPropagation();

        modal.style.display = 'none';
    });
});

// Listen to click event on the window ONCE, outside the forEach loop
window.addEventListener('click', () => {
    document.querySelectorAll(".modal").forEach(el => {
        el.style.display = 'none';
    });
});

You can see that I have left out the window click event listener in the for each loop. The reason is that you can simply rely on event.stopPropagation to stop the click event from bubbling to the window object. That means, you simply have to bind the click event once on the window.

Also, I would not suggest using Element.onclick = Function to add event listeners, because that means you can only assign one handler this way. Use Element.addEventListener instead.

Upvotes: 1

Related Questions