js Crear modal básico animado desde cero con html, css y vanilla javascript sin jquery
Modal animado sin jquery y sin bootstrap
El código consta de 3 partes: html, css y js. Esta última (el javascript) nos habilita la interacción con el "componente" aplicandole comportamientos: mostrar, ocultar etc.
Sobre el Js asociado tiene dos formatos. Uno con interacción directa y otro usando la función wrapper MyModal.
Este wrapper actua como una Clase instanciadora reutilizable y nos permite tener varios modal en la misma página.

De la siguiente forma:
//definición function MyModal(idModal, idOpener)
const mymodal = new MyModal("modal", "btn-open")
mymodal.show().hide()
const mymodal_2 = new MyModal("modal-2", "btn-open-2")
mymodal_2
.set_title("<span>Un titulo ramdom</span>")
.set_body("<p>Un ejemplo de cuerpo random</p>")
.show()
El código
html
<button type="button" id="btn-open">Open modal</button>
<button type="button" id="btn-open-2">Open modal object</button>
<div id="modal" class="modal-wrapper">
<div class="modal-dialog modal-dialog-grid" role="modal-dialog">
<header class="area-header">
<h2 role="title">Modal title</h2>
<button type="button" role="btn-close">x</button>
</header>
<div class="area-body" role="body">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ...
</p>
<p>
Aenean laoreet tempor mauris non vulputate. Sed ut erat erat. ...
</p>
</div>
</div>
</div>
<div id="modal-2" class="modal-wrapper">
<div class="modal-dialog modal-dialog-grid" role="modal-dialog">
<header class="area-header">
<h2 role="title">example title</h2>
<button type="button" role="btn-close">x</button>
</header>
<div class="area-body" role="body">
example body
</div>
</div>
</div>
Js 1
El código sin wrapper
const $btnOpen = document.getElementById("btn-open")
const $modal = document.getElementById("modal")
const $modalDialog = $modal.querySelector(":scope > [role='modal-dialog']")
const $btnClose = $modalDialog.querySelector(":scope > header > [role='btn-close']")
$btnOpen.addEventListener("click", () => {
$modal.classList.remove("modal-hide")
$modal.classList.add("modal-show")
})
$modal.addEventListener("click", () => $modal.classList.add("modal-hide"))
//si hacemos click en la zona blanca evitamos que llegue el evento al modalWrapper y se cierre el modal
$modalDialog.addEventListener("click", e => e.stopPropagation())
$btnClose.addEventListener("click", () => $modal.classList.add("modal-hide"))
Js 2
Con la función wrapper MyModal
//funcion tipo clase
function MyModal(idModal, idOpener=null) {
const $modal = document.getElementById(idModal)
if(!$modal) return console.log("no modal found!")
const $dialog = $modal.querySelector(":scope > [role='modal-dialog']")
const $title = $dialog.querySelector(":scope > header > [role='title']")
const $btnClose = $dialog.querySelector(":scope > header > [role='btn-close']")
const $body = $dialog.querySelector(":scope > [role='body']")
const $opener = idOpener ? document.getElementById(idOpener) : null
const show = () => {
$modal.classList.remove("modal-hide")
$modal.classList.add("modal-show")
}
const hide = () => $modal.classList.add("modal-hide")
this.show = function (fnBefore, fnAfter) {
if (fnBefore) {
const abort = fnBefore()
if (abort) return this
}
show()
if(fnAfter) fnAfter()
return this
}
this.hide = function (fnBefore, fnAfter) {
if (fnBefore) {
const abort = fnBefore()
if (abort) return this
}
hide()
if(fnAfter) fnAfter()
return this
}
this.set_body = function (html) {
if(!html || !$body) return this
$body.innerHTML = html
return this
}
this.set_title = function (html) {
if(!html || !$title) return this
$title.innerHTML = html
return this
}
this.destroy = () => {
if($modal) $modal.removeEventListener("click", hide)
if($opener) $opener.removeEventListener("click", show)
if($btnClose) $btnClose.removeEventListener("click", hide)
if($title) $title.innerHTML = ""
if($body) $body.innerHTML = ""
return null
}
(() => {
//configuro los listeners
$modal.addEventListener("click", hide)
if ($dialog) $dialog.addEventListener("click", e => e.stopPropagation())
if ($opener) $opener.addEventListener("click", show)
if ($btnClose) $btnClose.addEventListener("click", hide)
})()
}//MyModal
css
@import url("https://fonts.googleapis.com/css?family=Roboto");
.debug {
/*
usando la consola para agregar un borde a los elementos y ver sus limites
*/
border:1px dashed red;
}
body {
font-family: "Roboto", "sans-serif";
/*
para tener una referencia y poder trabajar con em y rem (ver el breakpoint)
*/
font-size: 16px;
}
/*
es el div de fondo negro
*/
.modal-wrapper {
background-color: rgb(0,0,0, .75);
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 10;
display: none;
/*
si bien con estos estilos se centra verticalmente usando flex, al hacer mas pequeña (en altura) la ventana
del navegador se mantiene centrado pero se pierde la parte superior e inferior por eso mejor se usa grid
*/
justify-content: center;
align-items: center;
}
.modal-show {
display: grid;
animation: anim-show .2s;
}
@keyframes anim-show {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
.modal-hide {
z-index: -1; /*hace que no se quede una capa sobre el boton de apertura que no permite hacer click*/
opacity: 0; /*esto permite que despues de la animacion se quede oculto*/
animation: anim-hide .25s;
}
@keyframes anim-hide {
from {
transform: scale(1);
opacity: 1;
}
to {
transform: scale(0);
opacity: 0;
}
}
/*
modal-dialog es la caja blanca donde va el contenido. El modal en sí.
*/
.modal-dialog {
background: #fff;
padding: 10px;
width: 37.5em;
min-height: 25em;
border-radius: 1%;
}
.modal-dialog-grid {
display: grid;
grid-template-rows: 3.44em calc(90vh - 3.44em);
grid-template-areas:
"area-header"
"area-body"
}
.area-header {
grid-area: area-header;
display: flex;
justify-content: space-between;
align-items: center;
}
.area-header button {
background: #0d6efd;
color: white;
width: 2em;
height: 2em;
border-radius: 1%;
border: 1px solid #86b7fe;
}
.area-header h2 {
margin: 0;
padding: 0;
padding-top: 0.15em;
position: sticky;
top:0;
}
.area-body {
grid-area: area-body;
margin: 0;
padding: 0;
}
/*
breakpoints de referencia que usa bootstrap
dejo de ejemplo solo este pero habria que tratarlo para los otros bp
*/
@media (max-width:575.98px){
body {
font-size: 14px;
}
.modal-dialog {
width: 30.63em;
}
}
@media (max-width:767.98px){
/*to-do*/
}
@media (max-width:991.98px){
/*to-do*/
}
@media (max-width:1399.98px){
/*to-do*/
}
El código fuente completo lo dejo en mi Github
Autor: Eduardo A. F.
Publicado: 09-11-2021 18:57