I\'m having some issues creating multiple modals on a single webpage following the sample code from w3schools.com. The code in question that I am using is this one: http://w
Update September 2020: The code in my answer is a slightly outdated version of the one in my blog post. Please view the post itself for the most recent version, along with explanations.
I know I'm necroing a dead post here, but I'd like to propose a cleaner, more maintainable solution. I wrote an in-depth guide on how to create multiple modals on a single page on my blog. The explanations are a bit involved, so if you want to understand how this code works, I break things down in detail in that post.
Two versions of the code are below, depending on what you want to accomplish: multiple stacked modals or multiple side-by-side modals.
I hope this helps anyone who comes across this in the future.
Creating stacked modals
// Stack of modals
let currentlyOpenModals = [];
const noModalsOpen = () => !currentlyOpenModals.length;
const openModal = modalId => {
const modalWrapper = document.getElementById(modalId);
modalWrapper.classList.add("visible");
currentlyOpenModals.push(modalWrapper);
};
// By definition, it's always the topmost modal that will be closed first
const closeTopmostModal = () => {
if (noModalsOpen()) {
return;
}
const modalWrapper = currentlyOpenModals[currentlyOpenModals.length - 1];
modalWrapper.classList.remove("visible");
currentlyOpenModals.pop();
};
const modalTriggers = document.querySelectorAll(".modal-trigger");
modalTriggers.forEach(modalTrigger => {
modalTrigger.addEventListener("click", clickEvent => {
const trigger = clickEvent.target;
const modalId = trigger.getAttribute("data-modal-id");
openModal(modalId);
});
});
// Otherwise, clicking the content of a modal will propagate the click to the modal wrapper,
// and that will close the entire thing. That's not what we want!
document.querySelectorAll(".modal-window").forEach(modal => {
modal.addEventListener("click", clickEvent => {
clickEvent.stopPropagation();
});
});
const modalWrappers = document.querySelectorAll(".modal-wrapper");
modalWrappers.forEach(modalWrapper => {
modalWrapper.addEventListener("click", () => {
closeTopmostModal();
});
});
document.querySelectorAll(".close-modal-button").forEach(closeModalButton => {
closeModalButton.addEventListener("click", () => {
closeTopmostModal();
});
});
document.body.addEventListener("keyup", keyEvent => {
if (keyEvent.key === "Escape") {
closeTopmostModal();
}
});
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
align-items: center;
display: flex;
flex-direction: column;
font-family: Arial;
font-size: 18px;
height: 100vh;
justify-content: center;
}
p {
margin-bottom: 1em;
}
.modal-wrapper {
align-items: center;
background-color: rgba(100, 100, 100, 0.5);
bottom: 0;
display: flex;
flex-wrap: wrap;
height: 100vh;
justify-content: center;
left: 0;
opacity: 0;
position: fixed;
right: 0;
transition: all 0.2s ease-in-out;
visibility: hidden;
width: 100%;
z-index: 1000;
}
.modal-wrapper.visible {
opacity: 1;
visibility: visible;
}
.modal-window {
background-color: white;
border-radius: 5px;
box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
padding: 20px;
transform: scale(0);
transition: 0.2s ease-in-out all;
}
.modal-wrapper.visible .modal-window {
transform: scale(1);
}
.modal-header {
align-items: center;
border-bottom: 2px solid black;
display: flex;
justify-content: space-between;
margin-bottom: 20px;
padding-bottom: 20px;
}
.close-modal-button {
align-items: center;
cursor: pointer;
display: flex;
height: 30px;
justify-content: center;
width: 30px;
}
.close-modal-button::before {
content: "X";
color: rgb(112, 112, 112);
}
.close-modal-button:hover::before {
color: black;
}
.modal-trigger, a {
color: rgb(10, 47, 255);
cursor: pointer;
text-decoration: underline;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="style.css" />
<title>Stacked Modals Demo</title>
</head>
<body>
<p>Lorem ipsum. <span class="modal-trigger" data-modal-id="modal1">Click this trigger</span> to open a modal.</p>
<p>Close a modal by clicking off to the side, clicking the X, or pressing Escape.</p>
<div class="modal-wrapper" id="modal1">
<section class="modal-window">
<header class="modal-header">
<h3>Title goes here...</h3>
<div class="close-modal-button"></div>
</header>
<p>Congrats, you've opened a modal!</p>
<p>Now open <span class="modal-trigger" data-modal-id="modal2">another modal</span>!</p>
</section>
</div>
<div class="modal-wrapper" id="modal2">
<section class="modal-window">
<header class="modal-header">
<h3>Modalception
You assigned the same Id to both modal triggering buttons. Different elements shouldn't have the same Id.
In you JavaScript code, you always pick a single element (a single close button, a single open button, etc).
It's a HTML/JS beginner's mistake I would say. This would work:
// Get the button that opens the modal
var btn = document.querySelectorAll("button.modal-button");
// All page modals
var modals = document.querySelectorAll('.modal');
// Get the <span> element that closes the modal
var spans = document.getElementsByClassName("close");
// When the user clicks the button, open the modal
for (var i = 0; i < btn.length; i++) {
btn[i].onclick = function(e) {
e.preventDefault();
modal = document.querySelector(e.target.getAttribute("href"));
modal.style.display = "block";
}
}
// When the user clicks on <span> (x), close the modal
for (var i = 0; i < spans.length; i++) {
spans[i].onclick = function() {
for (var index in modals) {
if (typeof modals[index].style !== 'undefined') modals[index].style.display = "none";
}
}
}
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target.classList.contains('modal')) {
for (var index in modals) {
if (typeof modals[index].style !== 'undefined') modals[index].style.display = "none";
}
}
}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content */
.modal-content {
position: relative;
background-color: #fefefe;
margin: auto;
padding: 0;
border: 1px solid #888;
width: 80%;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
-webkit-animation-name: animatetop;
-webkit-animation-duration: 0.4s;
animation-name: animatetop;
animation-duration: 0.4s
}
/* Add Animation */
@-webkit-keyframes animatetop {
from {top:-300px; opacity:0}
to {top:0; opacity:1}
}
@keyframes animatetop {
from {top:-300px; opacity:0}
to {top:0; opacity:1}
}
/* The Close Button */
.close {
color: white;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
.modal-header {
padding: 2px 16px;
background-color: #5cb85c;
color: white;
}
.modal-body {padding: 2px 16px;}
.modal-footer {
padding: 2px 16px;
background-color: #5cb85c;
color: white;
}
<h2>1st Modal</h2>
<!-- Trigger/Open The Modal -->
<button class="modal-button" href="#myModal1">Open Modal</button>
<!-- The Modal -->
<div id="myModal1" class="modal">
<!-- Modal content -->
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
<h2>Modal Header</h2>
</div>
<div class="modal-body">
<p>Some text in the Modal Body</p>
<p>Some other text...</p>
</div>
<div class="modal-footer">
<h3>Modal Footer</h3>
</div>
</div>
</div>
<h2>2nd Modal</h2>
<!-- Trigger/Open The Modal -->
<button class="modal-button" href="#myModal2">Open Modal</button>
<!-- The Modal -->
<div id="myModal2" class="modal">
<!-- Modal content -->
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
<h2>Modal Header</h2>
</div>
<div class="modal-body">
<p>Some text in the Modal Body</p>
<p>Some other text...</p>
</div>
<div class="modal-footer">
<h3>Modal Footer</h3>
</div>
</div>
</div>
On your JSFiddle, make sure you use class="myBtn"
instead of id="myBtn"
then it should work.
Here is the full working code:
HTML:
<h2>1st Modal</h2>
<!-- Trigger/Open The Modal -->
<button class="myBtn">Open Modal</button>
<!-- The Modal -->
<div id="myModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
<h2>Modal Header</h2>
</div>
<div class="modal-body">
<p>Some text in the Modal Body</p>
<p>Some other text...</p>
</div>
<div class="modal-footer">
<h3>Modal Footer</h3>
</div>
</div>
</div>
<h2>2nd Modal</h2>
<!-- Trigger/Open The Modal -->
<button class="myBtn">Open Modal</button>
<!-- The Modal -->
<div id="myModal2" class="modal">
<!-- Modal content -->
<div class="modal2-content">
<div class="modal-header">
<span class="close">×</span>
<h2>Modal Header</h2>
</div>
<div class="modal-body">
<p>Some text in the Modal Body</p>
<p>Some other text...</p>
</div>
<div class="modal-footer">
<h3>Modal Footer</h3>
</div>
</div>
</div>
JS:
// Get the modal
var modal = document.getElementsByClassName('modal');
// Get the button that opens the modal
var btn = document.getElementsByClassName("myBtn");
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close");
// When the user clicks the button, open the modal
btn[0].onclick = function() {
modal[0].style.display = "block";
}
btn[1].onclick = function() {
modal[1].style.display = "block";
}
// When the user clicks on <span> (x), close the modal
span[0].onclick = function() {
modal[0].style.display = "none";
}
span[1].onclick = function() {
modal[1].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";
}
}
Why are you using so much code, and javascript to do something you can do using just CSS and HTML? (simplified example, animations and such can of course be added)
[id^=modal] {
display: none;
border: 1px solid red;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
min-height: 3em;
min-width: 5em;
max-width: 10em;
}
input[type=checkbox] {
position: absolute;
clip: rect(0 0 0 0);
}
#modal1:target {
display: block;
}
#modal2:target {
display: block;
}
[id^=modal] a {
float: right;
}
<div id="content">
<p>This is the content-container</p>
<a href="#modal1">Open first modal</a><br>
<a href="#modal2">Open second modal</a>
<div id="modal1"><a href="#">Close</a>This is the first modal</div>
<div id="modal2"><a href="#">Close</a>This is the second modal</div>
</div>