How to get this script to work for multiple modal popups

The following is working great for one modal. But now I want to add another modal that works with a different button and I don’t know how to alter this code to accomplish that. Any ideas?

// Get the modal
var modal = document.getElementById('myModal');

// Get the button that opens the modal
var btn = document.getElementById("myBtn");

// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close")[0];

// When the user clicks the button, open the modal 
btn.onclick = function() {
    modal.style.display = "block";
}

// When the user clicks on <span> (x), close the modal
span.onclick = function() {
    modal.style.display = "none";
}

// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
    if (event.target == modal) {
        modal.style.display = "none";
    }
}

You’d need to use a class instead of an id. Can you post a demo of what you have (i.e. the HTML and CSS to accompany the JS). You could use JSFiddle or CodePen to do this.

Hi, yes of course. Here is a demo: http://codepen.io/aro_007/pen/NdzXeW

Something like this?

The code is a bit flaky in so far as it won’t work too well in old browsers, but if browser support is something you care about, I’d use jQuery (I can rewrite it to use jQuery if you like).

Also, the close button is not focussable when using a keyboard. You might want to sort that out? I go into that a little bit here.

Yes that’s exactly what I’m looking for!

For browser support, I’m really mostly concerned with the industry standard. For IE, I usually look at IE10 and newer. Do you see issues with this code still for those parameters?

The updated [flakey] code won’t work on IE or Edge, or most Android devices, which is based on the use of the Element.closest property. http://caniuse.com/#search=closest

If you are focused on IE10 compatibility then other techniques will need to be used.

Do you see issues with this code still for those parameters?

Yup. As Paul says .closest(); isn’t supported in IE10 or Edge. Here’s the jQuery version. This’ll work everywhere. I’ve used jQuery 3, but if you use jQuery 2.x it’ll work all the way back to IE8:

$(".button").on("click", function() {
  var modal = $(this).data("modal");
  $(modal).show();
});

$(".modal").on("click", function(e) {
  var className = e.target.className;
  if(className === "modal" || className === "close"){
    $(this).closest(".modal").hide();
  }
});

As some will be quick to point out, using jQuery for just this single feature is overkill. Nonetheless, it’s what I would do if you care about browser support and the minute you need to add more JS features to your website, you can use it again.

@Paul_Wilkins: if you’re feeling like a challenge, feel free to convert this to vanilla JS which will work in all modern browsers. It would actually be quite interesting to compare.

What do we consider to be modern browsers? At the very least, the ES6 features can be removed, which are the spread operator and the closest method…

The spread operator can be updated to:

// var modalBtns = [...document.querySelectorAll(".button")];
var modalBtns = document.querySelectorAll(".button");

and the closest method, which is currently experimental, can be polyfilled with the code from https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill

After some extreme refactoring to make the code easier to understand, and removing the prototype technique to keep JSLint happy, we end up with the following closestEl() function:

    function closestEl(el, selector) {
        var doc = el.document || el.ownerDocument;
        var matches = doc.querySelectorAll(selector);
        var i;
        while (el) {
            i = matches.length - 1;
            while (i >= 0) {
                if (matches.item(i) === el) {
                    return el;
                }
                i -= 1;
            }
            el = el.parentElement;
        }
        return el;
    }

which we use with:

var modal = closestEl(btn, ".modal");

That works back to IE8, which is good enough for modern browser support.

The code that you end up with is:

/*jslint browser*/
/*global window*/
(function iife() {
    "use strict";
    function closestEl(el, selector) {
        var doc = el.document || el.ownerDocument;
        var matches = doc.querySelectorAll(selector);
        var i;
        while (el) {
            i = matches.length - 1;
            while (i >= 0) {
                if (matches.item(i) === el) {
                    return el;
                }
                i -= 1;
            }
            el = el.parentElement;
        }
        return el;
    }
    var modalBtns = document.querySelectorAll(".button");
    modalBtns.forEach(function addBtnClickEvent(btn) {
        btn.onclick = function showModal() {
            var modal = btn.getAttribute("data-modal");
            document.getElementById(modal).style.display = "block";
        };
    });

    var closeBtns = document.querySelectorAll(".close");
    closeBtns.forEach(function addCloseClickEvent(btn) {
        btn.onclick = function closeModal() {
            var modal = closestEl(btn, ".modal");
            modal.style.display = "none";
        };
    });

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

Thank you so much! I really appreciate you both for taking a look at this for me.

Interesting stuff, Paul. Thanks :slight_smile:

No worries. Out of interest, which solution did you go with?

I will preface this by saying I have very limited Javascript knowledge. A little more experience with jQuery, but not much.

I wanted to go the vanilla Javascript route, because I’ve heard the industry is moving away from jQuery.

However, I don’t know if I did something wrong, but I couldn’t get the popups to work with the Javascript posted by @Paul_Wilkins. So I implemented the jQuery script and it is working.

That’s alright - here’s a jsfiddle that demonstrates that the no-library version works perfectly well.

Hey @aro_007, thanks for the follow up :slight_smile:

Although modern browsers are becoming more and more capable, jQuery is as relevant now as it ever was.

It’s true, there have been a lot of “anti-jQuery” articles of late (example) and essentially their message is correct: don’t automatically reach for a library when you just need a few choice operations you can and should learn to implement yourself. Saying that however, jQuery is battle tested (so browser support is a non-issue), it makes the ugly native DOM API considerably nicer/easier to work with and by the time it comes to something like Ajax, it’s basically a no brainer.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.