Ventanas modales personalizadas
Como programar e implementar nuestros propios alert() y confirm() con Javascript puro
Una ventana modal (modal window) o cuadro de dialogo (dialog box)
es el elemento de la interfaz de usuario que usamos cuando queremos centrar la
atención del usuario en un elemento en particular que esta ligado a alguna acción en concreto, este
elemento se situara por encima del demás contenido.
Por otro lado un lightbox es el efecto de oscurecer todo el contenido en nuestra
interfaz a excepción de un único elemento que se busca destacar por sobre las demás cosas.
Aunque una ventana modal no tiene que ir siempre acompañada del efecto lightbox en esta ocasión si
que usaremos ambas cosas.
Maquetando la estructura base
Antes de ponernos a generar código HTML desde Javascript nos viene bien primero escribir a mano la
estructura para tenerla de referéncia.
<div class="liwindow-container">
<div class="liwindow-card">
<div class="liwindow-header">
<h4>Estas a punto de eliminar un archivo</h4>
<button type="button" class="liwindow-close">×</button>
</div>
<div class="liwindow-body">Realmente deseas continuar?</div>
<div class="liwindow-footer">
<button type="button" class="liwindow-btn liwindow-cancel">Cancelar</button>
<button type="button" class="liwindow-btn liwindow-acept">Aceptar</button>
</div>
</div>
</div>
Usaremos un contenedor general (.liwindow-container) que sera el que oculte todo el
demás contenido de la web mostrandose semiopaco.
Dentro pondremos el cuadro (.liwindow-card) que contendrá los elementos de nuestra
ventana modal.
Para el caso de las ventanas tipo confirm() usaremos un header
(.liwindow-header) donde pondremos el titulo de la ventana modal y el botón de
cierre (.liwindow-close).
Después viene el contenedor del cuerpo de la ventana (.liwindow-body) y por ultimo
el pie (.liwindow-footer) donde pondremos los botones
(.liwindow-btn) para aceptar (.liwindow-acept) o cancelar
(.liwindow-cancel).
Aplicando los estilos
/* -- liwindow -- */
.liwindow-container *, .liwindow-container *::after, .liwindow-container *::before{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.liwindow-container{
position: fixed;
top: 0;
left: 0;
background: rgba(0,0,0,0.5);
width: 100%;
height: 100%;
z-index: 200;
}
/* -- liwindow Modal Dialog -- */
.liwindow-card{
margin: 0 auto;
margin-top: 40px;
width: 500px;
max-width: 100%;
box-shadow: 0px 0px 25px -5px #333;
}
.liwindow-header, .liwindow-footer, .liwindow-body{
background-color: #fff;
}
.liwindow-header{
display: flex;
flex-direction: row;
justify-content: space-between;
}
.liwindow-close{
padding: 20px;
display: inline-block;
font-size: 1.33rem;
color: #333;
font-weight: bold;
background-color: transparent;
border: none;
transition: 0.3s all;
}
.liwindow-close:hover, .liwindow-close:focus{
color: #7C4DFF;
background-color: #f3f3f3;
}
.liwindow-close:active{
color: #7C4DFF;
transform: translate(-3px, -3px);
transform: scale(0.9);
}
.liwindow-close:focus{
outline: 1px solid #00BCD4;
}
.liwindow-body{
padding: 20px;
color: #333;
font-size: 18px;
}
.liwindow-header h4{
color: #333;
padding: 20px;
display: inline-block;
font-size: 25px;
font-weight: bold;
}
.liwindow-footer{
text-align: right;
}
.liwindow-btn{
padding: 20px;
background-color: transparent;
border: none;
color: #9c27b0;
text-transform: uppercase;
font-size: 15px;
transition: 0.3s all;
}
.liwindow-btn:hover, .liwindow-btn:focus{
color: #7C4DFF;
background-color: #f3f3f3;
}
.liwindow-btn:active{
color: #7C4DFF;
transform: translate(-3px, -3px);
transform: scale(0.9);
}
.liwindow-btn:focus{
outline: 1px solid #00BCD4;
}
.liwindow-hide{
display: none;
}
Aplicando estos estilos CSS anteriores a nuestra estructura HTML tendríamos la siguiente apariencia:
Definiendo las clases y métodos
/* -- Clase principal para el modal window -- */
class liwindow {
constructor(html)
/* Metodo para abrir lightbox */
show()
/* Metodo para cerrar lightbox*/
hide()
/* Metodo para agregar contenido al lightbox*/
setContent()
/* Metodo para remover el lightbox y su contenido del html*/
remove()
}
/* -- Alert Modal Window -- */
class lialert extends liwindow {
}
/* -- Confirm Modal Window -- */
class liconfirm extends liwindow {
}
Partiremos de una clase general con los métodos necesarios para el funcionamiento de nuestras
ventanas modales, crearemos una clase heredera por cada elemento con el efecto lightbox que
deseemos. En este caso una clase para nuestros custom alert() y otra para nuestros custom
confirm().
Lógica de la clase principal
/* -- Clase para lightbox y modal dialogs -- */
class liwindow {
constructor(html) {
/* Creamos el contenedor principal del lightbox */
this.container = document.createElement("div");
this.container.setAttribute("class", "liwindow-container liwindow-hide");
this.container.addEventListener("click", this.hide);
document.body.appendChild(this.container);
/* Añadimos el html proporcionado */
if (html != null) this.setContent(html);
}
/* Metodo para abrir lightbox */
show = () => {
this.container.classList.remove("liwindow-hide");
document.body.style.overflow = "hidden";
};
/* Metodo para cerrar lightbox */
hide = () => {
this.container.classList.add("liwindow-hide");
document.body.style.overflow = "auto";
};
/* Metodo para agregar contenido al lightbox */
setContent = (cont) => {
if (typeof cont == "string") {
this.container.innerHTML = cont;
this.container.firstChild.addEventListener("click", function (e) {
e.stopPropagation();
});
} else {
cont.addEventListener("click", function (e) {
e.stopPropagation();
});
this.container.appendChild(cont);
}
};
/* Metodo para remover el lightbox y su contenido del html */
remove = () => document.body.removeChild(this.container);
}
asignadas a propiedades de clase, funcionaran igual que métodos normales, con el añadido de que
cualquier uso de this dentro de ese contexto siempre hace referencia a la clase, lo que es
realmente necesario para poder llamar las propiedades de nuestra clase desde los
addEventListener que usaremos.
Desde el constructor principal generaremos parte del HTML previamente descrito y lo
insertaremos en el documento html, pero este contenido estará oculto inicialmente (usando la clase:
.liwindow-hide).
Para añadir contenido a nuestra ventana usamos el método setContent(), el contenido
dependerá del componente final (alert() o confirm()), si estamos añadiendo texto
plano este se insertara con innerHTML, de lo contrario se añadirá con appendChild.
Nuestra ventana modal estará oculta hasta que se mande a llamar el método show(), el
cual quitara la clase que mantiene oculto el contenido, ademas de modificar la propiedad overflow
del body del documento para evitar que nuestra ventana modal sea evadida al hacer scroll.
El método hide() por otro lado se encargara de volver a ocultar la ventana modal,
añadiendo la clase .liwindow-hide y devolviendo el overflow del body a su estado original.
Desde el inicio estamos enlazando el método hide() a nuestro contenedor principal
(.liwindow-container), de modo que al hacer click sobre el automáticamente se cierre la ventana
modal, pero para evitar que inesperadamente se cierre al hacer click al contenido de nuestro cuadro
de dialogo debemos usar stopPropagation en dicho contenido.
Lógica de nuestro custom alert
/* -- Alert Modal Window -- */
class lialert extends liwindow {
constructor(body) {
const cont = `<div class="liwindow-card">
<div class="liwindow-body">${body}</div>
<div class="liwindow-footer">
<button type="button" class="liwindow-btn liwindow-acept">Aceptar</button>
</div>
</div>`;
super(cont);
this.container
.querySelector(".liwindow-acept")
.addEventListener("click", this.hide);
}
}
La lógica de nuestro alert es muy simple, solo estamos definiendo el HTML que llevará el cuadro de
dialogo concatenando el mensaje que se mostrará, y todo este código lo mandamos al contenedor
principal del lightbox pasandolo como argumento del constructor de la clase principal.
Ya que se haya insertado el HTML, al botón que ahí mismo definimos le enlazamos el funcionamiento del
método hide().
Lógica de nuestro custom confirm
/* -- Confirm Modal Window -- */
class liconfirm extends liwindow {
constructor(param) {
const cont = `<div class="liwindow-card">
<div class="liwindow-header">
<h4>${param.header}</h4>
<button type="button" class="liwindow-close">×</button>
</div>
<div class="liwindow-body">${param.body}</div>
<div class="liwindow-footer">
<button type="button" class="liwindow-btn liwindow-cancel">Cancelar</button>
<button type="button" class="liwindow-btn liwindow-acept">Aceptar</button>
</div>
</div>`;
super(cont);
this.container
.querySelector(".liwindow-close")
.addEventListener("click", this.hide);
this.container
.querySelector(".liwindow-cancel")
.addEventListener("click", () => {
this.hide();
param.action(false);
});
this.container
.querySelector(".liwindow-acept")
.addEventListener("click", () => {
this.hide();
param.action(true);
});
}
}
La lógica de nuestro confirm es similar a la anterior, solo que aquí tenemos más HTML, exactamente el
mismo HTML que maquetamos al inicio de todo, ademas de que en el caso anterior teníamos un solo
botón y ahora tenemos 3.
Primero enlazamos el método hide() tanto a nuestro botón de cerrar.
Después enlazamos un callback tanto al botón botón Aceptar, como al botón de cancelar, con la
diferencía de que pasaremos como argumento true o false según la acción
seleccionada por el usuario.
Modo de uso
Solo debemos enlazar el código CSS y JS a nuestro documento HTML para empezar a crear tantas
instancias de nuestras ventanas modales como necesitemos.
const myalert = new lialert("Una alerta simple");
Para para poder abrir nuestra ventana modal solo necesitamos llamar al método
show(), esto lo podemos hacer desde cualquier otra parte de nuestro código
javascript, por ejemplo al hacer click a algún botón.
document.querySelector("#showalert").addEventListener("click", myalert.show);
Lo mismo para nuestro confirm, con la diferencía de que debemos pasar como argumento un objeto que
contenga todo lo que mostraremos en el cuadro de dialogo del confirm.
const myconfirm = new liconfirm({
header: "Estas a punto de eliminar un archivo",
body: "Realmente deseas continuar?",
action: (result) => {
/* Eliminar archivo en caso de aceptar */
}
});
A continuación mostrare el script final y ejemplos de funcionamiento, todo esto estará en Codepen por
si deseas exportarlo e incluso modificarlo.
Código final y resultado
See the Pen
Custom Alert/Confirm Vanilla JS by Ulises Rendon (@fdulises)
on CodePen.


