Eventos de Clic y Modales en JavaScript: Interactividad Básica
Resumen
¿Cómo se manejan eventos en JavaScript?
Los eventos en JavaScript son fundamentales para interactuar con los usuarios y responder a sus acciones. Uno de los eventos más comunes es el evento 'clic'. Este evento es esencial en cualquier interacción que requiera abrir ventanas emergentes o "modales". Aquí veremos cómo vincular un evento de clic a un botón para gestionar la apertura de un modal.
¿Cómo implementar un evento de clic?
Para implementar el evento de clic, necesitas asociar una función que será ejecutada al hacer clic en un botón. Esto lo logras con el método addEventListener. En el contexto de nuestro ejemplo, vamos a asociar el botón con una función al hacer clic, modificando clases CSS para mostrar el modal.
Este fragmento de código elimina la clase 'hidden' y añade la clase 'visible', logrando así que el modal se muestre. Las clases 'hidden' y 'visible' controlan la visibilidad del modal al gestionar propiedades de estilo como display y opacity.
¿Cómo gestionar la visibilidad del modal?
Para asegurarte de que el modal tenga la apariencia deseada y se posicione correctamente en la pantalla, es crucial trabajar con las clases CSS. Aquí es donde display: grid se vuelve útil, ya que permite centrar los elementos dentro del modal.
La función place-items: center; centra el contenido horizontal y verticalmente, asegurando que tu modal sea estéticamente agradable en la interfaz.
¿Cómo cerrar el modal?
Cerrar el modal es un proceso similar al que usamos para abrirlo. Podemos incluir un icono de cierre (por ejemplo, una "x") y asociarle un evento de clic que oculte el modal. Aquí es donde entra el uso de icons8.com para obtener íconos gratuitos que puedan ser integrados en tu proyecto.
Añadiendo un ícono para cerrar el modal
El ícono de cierre se obtiene e incluye en el HTML. Usando el siguiente código, asociamos un evento de clic que oculta el modal al remover la clase 'visible' y añadir 'hidden'.
Consideraciones sobre la especificidad y el uso de !important
Al modificar clases para controlar la visibilidad, puede surgir un problema de especificidad CSS. A veces es necesario usar !important para asegurar que las reglas sean aplicadas independientemente de las otras clases.
Aunque !important no siempre es buena práctica, en algunos casos, como este, es útil para superar problemas de especificidad.
Desafío: Practicando con otros elementos
Una excelente manera de solidificar tu entendimiento es practicar asociando eventos de clic a otros botones o elementos en la misma página. Intenta replicar este comportamiento de modal con diferentes superhéroes, creando modales independientes para cada uno de ellos.
No olvides explorar y experimentar con variaciones de diseño y funcionalidad. ¡Continúa practicando y mejorando tus habilidades en JavaScript para crear interfaces de usuario dinámicas y atractivas!
Para el ejemplo no necesariamente tienen que utilizar el !important , solo pongan que el display sea manejado únicamente por las clases .hidden y .visible
Para no crear un evento para cada super heroe en lugar de id podriamos usar una misma clase para los 3 superheroes, y usar querySelectorAll
Definitivamente es una buena idea en este caso, dejo otras dos formas de realizarlo.
La segunda me gusta más para esta app, pero la primera puede ser muy util si necesitamos realizar la misma acción con los tres elementos y al mismo tiempo tener la posibilidad de manejarlos de manera individual.
Icono de cerrar:
Cambie un poco el diseño para no tener exactamente el mismo del curso.
HTML
<!DOCTYPE html><html lang="es"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="style.css"><title>Document</title></head><body><div class="page"><!--MainContent--><section class="container"><nav class="navbar"><div class="navbar__content"><img src="./img/logo.svg" alt="logo spiderman"><ul><li><a href="">Exclusivos</a></li><li><a href="">Nuevos</a></li><li><a href="">GiftCards</a></li><li><a href="">BuscaUnaTienda</a></li></ul></div></nav><section class="main-content"><p>Diseña tu</p><h2>Spiderman!</h2><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.Necessitatibus dolores sint nobis numquam, eos accusantium ducimus aut quisquam pariatur, consectetur labore ut hic voluptates asperiores quidem, corrupti error ullam amet.</p></section><section class="side-content"><div></div><p>Spiderman</p><img src="./img/spidermanAside.png" alt=""></section><section class="footer"><div class="openModal"><img src="./img/spidermanFooter1.png" alt="spiderman"></div><div class="openModal"><img src="./img/spidermanFooter2.png" alt="spiderman"></div><div class="openModal"><img src="./img/spidermanFooter3.png" alt="spiderman"></div></section></section><!--Loader--><section class="loader"><div><img src="./img/load.svg" alt="imagen logo spiderman"></div><div><img src="./img/load.svg" alt="imagen logo spiderman"></div><div><img src="./img/load.svg" alt="imagen logo spiderman"></div></section><!--Modal--><section class="modal hidden"><div class="modal__content hidden"><img class="modal__content--close" src="https://img.icons8.com/external-flat-icons-inmotus-design/67/000000/external-close-basic-work-elements-flat-icons-inmotus-design.png"/></div></section></div></body><script src="app.js"></script></html>
CSS
/*
1. Posicionamiento
2. Modelo de caja
3. Tipografia
4. Visuales
5. Otros
*/@importurl("https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap");:root{--primario:#222831;--secundario:#393E46;--terciario:#00ADB5;--acentuar:#EEEEEE;--spiderman:#a71814;}html{ font-size:62.2%; font-family:"Roboto", sans-serif;}*{ box-sizing: border-box;margin:0;padding:0; text-decoration: none; list-style: none;}body{ font-size:1.5rem;height: 100vh;overflow: hidden;background:var(--primario);}body,a{color:var(--acentuar);}/* LOADER */.page{width:100%;height: 100vh;position: relative;}.container{position: absolute; z-index:1;}.loader{width:100%;height:100%;display: flex; align-items: center; justify-content: center; flex-direction: row;background:var(--primario);position: absolute; z-index:3;}.loader div{width: 8rem;height: 8rem;/* border-radius: 50%; */margin: 1rem; background-image:url();}/* LOADER - ANIMATION *//* FONDO */.loader{animation: loader 3s linear forwards;opacity:1;visibility: visible;}@keyframes loader{0%,95%{opacity:1;visibility: visible;}100%{opacity:0;visibility: hidden;}}/* PUNTOS DE CARGA */.loader div{animation: scaling 1.5s linear infinite;transform:scale(1);}.loader div:nth-child(1){ animation-delay:0.2s;}.loader div:nth-child(2){ animation-delay:0.5s;}.loader div:nth-child(3){ animation-delay:0.8s;}@keyframes scaling {0%,100%{transform:scale(1);}30%,80%{transform:scale(0.5);}}/* GRID PRINCIPAL */.container{width:100%;height:100%;display: grid; grid-template-columns:55%45%; grid-template-rows:20%50%30%; grid-template-areas:"nav aside""section aside""footer aside"}/* NAVBAR */.navbar{width:100%;height:100%;display: flex; place-content: center; grid-area: nav; border-bottom:.1rem solid var(--acentuar);}/* POSICIONAMIENTO DE NAVBAR */.navbar__content{width:100%;padding:000 8rem;display: flex; flex-direction: row; align-items: center;gap: 2rem; font-weight:300;}/* ORGANIZANDO EL LOGO */.navbar__content img{width: 6rem;padding:.8rem;border:.1rem solid var(--acentuar); border-radius:1.2rem;background:var(--spiderman);}/* ORGANIZANDO LAS RUTAS/URL/OPCIONES DE NUESTRO MENU */.navbar__content ul{width:100%; margin-block-start:.8rem;display: flex; justify-content: space-between;}.navbar__content ul li{padding:.8rem; border-radius: 1rem; font-weight:700;}.navbar__content ul li:first-child{border:.1rem solid var(--acentuar);color:var(--secundario);background:var(--terciario);}.navbar__content ul li:hover{border:.1rem solid var(--acentuar);background:var(--terciario);}.navbar__content ul li:hover > a {color:var(--primario);}/* NAVBAR - ANIMATION */.navbar__content{animation:1.5s navbar 3s ease-in-out forwards;transform:translateY(-20rem);}@keyframes navbar {0%{transform:translateY(-20rem);}100%{transform:translateY(0);}}/* MAIN CONTENT */.main-content{ grid-area:section ;display: flex; flex-direction: column; justify-content: center;padding:000 8rem;}.main-content p:first-child{margin:00-1.4rem 2.8rem; font-size:3.6rem; font-weight:300;}.main-content h1{margin:002.8rem 0; font-size:4.8rem; font-weight:700;}.main-content p:last-child{width:80%; font-weight:100;}/* MAIN CONTENT - ANIMATION */.main-content{animation:1.5s main-content 3s ease-in-out forwards;transform:translateX(-100rem);}@keyframes main-content {0%{transform:translatex(-100rem);}100%{transform:translateY(0);}}/* SIDE CONTENT */.side-content{ grid-area: aside;display: grid; grid-template-columns:repeat(6,1fr); align-items: center;}/* SIDE CONTENT TEXT */.side-content p{ font-size:3.6rem; grid-row:1/2; grid-column:1/2;transform:rotate(-90deg);}/* MAIN CONTENT TEXT - ANIMATION */.side-content p{animation:1.5s side-content-text 4s ease-in-out forwards;opacity:0;}@keyframes side-content-text {0%{opacity:0;}100%{opacity:1;}}/* SIDE CONTENT IMG */.side-content img{width:100%; grid-row:1/2; grid-column:2/6;}/* MAIN CONTENT IMG - ANIMATION */.side-content img{animation:1.5s side-content-img 4s ease-in-out forwards;transform:translateX(200rem);}@keyframes side-content-img {0%{transform:translateX(200rem);}80%{transform:translateX(-5rem);}100%{transform:translateX(0rem);}}/* SIDE CONTENT DIV */.side-content div{width:100%;height:100%; grid-row:1/2; grid-column:4/7;background:var(--acentuar);}/* FOOTER */.footer{display: flex; justify-content: space-evenly; align-items: flex-end; border-radius:0 8rem 00;background:var(--acentuar);}/* FOOTER - ANIMATION */.footer{animation:1.5s footer 4s ease-in-out forwards;transform:translateY(200rem);}@keyframes footer{0%{transform:translateY(200rem);}100%{transform:translateY(0rem);}}/* FOOTER DIV */.footer div {display: flex; justify-content: center;width: 12rem;height:50%; border-radius: 2rem 2rem 00; background-color:var(--terciario);}/* FOOTER DIV - ANIMATION */.footer div {animation:1.5s footer-div 5s ease-in-out forwards;transform:translateY(200rem);}.footer div:nth-child(1){ animation-delay: 5s;}.footer div:nth-child(2){ animation-delay:5.3s;}.footer div:nth-child(3){ animation-delay:5.6s;}@keyframes footer-div{0%{transform:translateY(200rem);}100%{transform:translateY(0rem);}}/* FOOTER IMG */.footer div img{width: 16rem;position: absolute;bottom:6.5rem;transform:translateY(0rem)scale(1);transition: transform 0.5s ease-in-out;}.footer div:hover > img{cursor: pointer;transform:translateY(-2rem)scale(1.3);transition: transform 0.3s ease-in-out;}/* FOOTER IMG - ANIMATION */.footer div img{animation:1.5s footer-img 6.5s ease-in-out forwards;transform:translateY(200rem);}.footer div:nth-child(1)> img{ animation-delay: 6s;}.footer div:nth-child(2)> img{ animation-delay:6.3s;}.footer div:nth-child(3)> img{ animation-delay:6.6s;}@keyframes footer-img{0%{transform:translateY(200rem);}100%{transform:translateY(0rem);}}/* MODAL */.hidden{display: none;}.visible{display: grid;}.modal{ place-items: center;width:100%;height:100%;position: absolute;background:rgba(0,0,0,0.5); z-index:3;}.modal__content{width:50%;height:50%;background:var(--secundario);}
considero que sería más sencillo solo agregar y quitar la clase hidden y así solo en JS agregas y quitas esa clase para no tener que utilizar Important o remove y add en una misma función. 💚
Mi solución con un metodo forEach y querySelectorAll
¿Quién más pensó que sería buena idea que Estefany haga el curso de JS tambien 😍?
Creo que no es necesario el uso del !important en la clase modal, solo es no agregar el parámetro grid y dejarle esa tarea a la clase visible, solo en modal usar el place-items.
++Modal: evento click con JavaScript++
Código de la clase:
Según entendí por @erickgomez (gracias :D).
1.- NO hace falta poner !important, porque recordemos que es una mala práctica.
2.- Estaba bien como la profesora lo estaba haciendo, el único "error" fue de que inicializamos nuestra clase modal diferente de none osea le decíamos que aparezca en el DOM.
3.- Para arreglar esto debíamos quitar el display:grid o cambiarlo por display:none de nuestra clase .modal , quedando:
Antes:
Si, no es necesario repetir tanto, podedes usar solamente una función, en la cual escuchas el contenedor padre, y con if saber si toco en algún "boton",y para el close lo mismo. Esto podes aprender buscando traversing javascript
Funciones útiles que conocer serian children(), parentElement(), closset(). this
También podrías hacer un querySelectorAll y un forEach entre todos los .modal donde obtener una referencia para saber cual modal abrir, se suele hacer con un data-atribute.
Me gustaria pasarte codigo pero como no sé como esta estructurado tu html puede que no te sirva de nada.
Muchas gracias, lo buscaré
porq el script no me funciona si lo paso a un archivo aparte ?
ya he echo el src en el html y lo compruebo con un alert() pero no me funciona nada así
buen día, podrías mostrar tu código para poder analizarlo
en el html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="style.css"><script src="script.js"></script></head><body><div class="page"><!-- main contene --><section class="container"><nav class="navbar"><div class="navbar__content"><img src="https://i.ibb.co/MMmDPXN/lego.png" alt="logo_lego"><ul><li><a href="#">Exclusivos</a></li><li><a href="#">NUEVOS</a></li><li><a href="#">GIFTCARDS</a></li><li><a href="#">FINDASTORE</a></li></ul></div></nav><section class="main-content"><p>Diseña tu</p><h1>SÚPERHÉROE</h1><p>Lorem ipsum dolor sit amet consectetur adipisicing elit.Iure, porro quidem nam reiciendis sapiente atque, voluptatibus et error dignissimos, aspernatur rerum tenetur enim!Alias, eos similique blanditiis molestiae pariatur impedit.</p></section><section class="side-content"><div></div><p>Superman</p><img src="https://i.ibb.co/x16pz1x/super-man.png" alt="Superman"></section><section class="footer"><div id="button1"><img src="https://i.ibb.co/tKWqw8J/spiderman.png" alt="spiderman"></div><div><img src="https://i.ibb.co/Xzsdvgg/robin.png" alt="robin"></div><div><img src="https://i.ibb.co/M18p91c/batman.webp" alt="batman"></div></section></section><!-- loader --><section class="loader"><div></div><div></div><div></div></section><!-- modal --><section class="modal hidden"><div class="modal__content"></div></section></div></body></html>
.modal.visible es cuando elemento tiene la clases "modal visible"
.modal.hidden es cuando el elemento tiene las clases
"modal hidden"
por ejemplo:
.padre .visible aqui estamos seleccionando un elemento con la clase "visible" que se encuentra dentro de otro elemento con la clase "padre"
.padre.visible aqui hacemos referencia a un elemento tiene las clases "padre visible"
Consejo :)
Actualmente estoy trabajando para un proyecto de factoring como frontend developer y puedo decir que si es mala practica el !Important ,no considero excepciones al respecto , debido a desarrolladores pasados me he topado con código de CSS muy dificil de manipular o directamente inmanipulable esto debido a !important regados por ahí ,recordemos que el código debe ser siempre reusable y escalable ,así que bajo ninguna circunstancia usen !important ,no notaran el problema si no hasta que el proyecto tenga miles y miles de líneas de código en solo estilos
Solucioné de la siguiente manera los eventos de click para cada imagen.
Y los usé desde Javascript trayendolos con un querySelectorAll(). Lo que me retorna un NodeList para así poder usar el metodo forEach() y recorrerlos escribiendo un solo evento para cada uno.
<section class="modal hidden"><div class="modal__content"><button
class="modal__close-btn" id="modal__close-btn" title="button to close modal"></button></div></section>
.
Estilos usando Sass:
.
JS
No es necesario el !important adema's he agregado a que cuando le demos click en el back del modal se cierre también:
Recuerden que el css el navegador procesa los estilos desde el primero hasta el ultimo, si primero colocan los estilos del .hidden y luego .vissible, al momento de renderizar siempre quedaran los del .visible porque es el ultimo en cargar
Para no agregar el evento a cada div podemos aplicar la delegación de servicios, incluso sin usar el id en los elementos:
La delegación de servicios es muy importante de entender y aplicar en proyectos donde podríamos tener sientos o miles elementos donde necesitamos hacer click para algún evento asi evitamos sobrecargar la memoria y mejorar el rendimiento.
En resumen estamos agregando un solo evento al padre contenedor y el nos avisara que hijo, nieto o incluso bisnieto disparo el evento:
Otra forma de resolver el problema del modal que es visible desde el inicio es darle un poco mas de especificidad usando el elemento html y su clase para no usar important.