The following illustrates how to code a Modal without requiring any libraries or framework.
Prerequisites, create the three files :
news.html,
contact.html,
about.html
and place them at the root of your project along wit the following files.
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Dynamic Content Loading with Modal</title> <link rel="stylesheet" href="style.css"> </head> <body> <div id="overlay"></div> <h1>Custom Modal in vanilla JS</h1> <nav> <a href="" class="modal-trigger" data-url="news.html" data-color="#005f73"> News </a> | <a href="" class="modal-trigger" data-url="about.html" data-color="#9b2226"> About </a> | <a href="" class="modal-trigger" data-url="contact.html" data-color="#ff8800"> Contact </a> </nav> <div style="width: 400px; margin: 0px auto;"> <p style="text-align: left;"> Create the following files in the same directory : <p> news.html, about.html, contact.html.</p> <p style="text-align: left;">Then open this file (index.html) in a web browser</p> <p style="text-align: left;"> Place stylde.css and app.js in the same directory as well. </p> </div> <div id="modal" class="modal" role="dialog" aria-modal="true"> <button id="modal-close-btn" class="close-btn">×</button> <div id="modal-content" class="modal-content"> <!-- Content will be loaded here --> </div> </div> <script src="app.js"></script> </body> </html>
style.css
/* ========================================================================== Styles Généraux ========================================================================== */ body { height: 100%; font-family: sans-serif; margin: 0; /* Enlève les marges par défaut du navigateur */ text-align: center; } /* ========================================================================== Overlay (Fond semi-transparent) ========================================================================== */ #overlay { /* État initial : caché */ opacity: 0; visibility: hidden; /* Géométrie et positionnement */ position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); z-index: 100; /* Animation de fondu */ transition: opacity 0.4s ease-in-out, visibility 0.4s ease-in-out; } #overlay.is-visible { /* État final : visible */ opacity: 1; visibility: visible; } /* ========================================================================== Fenêtre Modale ========================================================================== */ /* --- 1. Styles de base (communs à mobile et desktop) --- */ .modal { /* NOUVEAU : On utilise la variable CSS. Si elle n'existe pas, la couleur par défaut est 'deeppink'. */ background: var(--modal-background, deeppink); position: fixed; z-index: 101; /* Doit être au-dessus de l'overlay */ box-sizing: border-box; /* Simplifie la gestion des dimensions */ /* État initial pour l'animation : complètement transparent et non-interactif */ opacity: 0; visibility: hidden; /* NOUVEAU : On anime aussi le changement de 'background' pour une transition de couleur fluide. */ transition: transform 0.4s ease-in-out, opacity 0.4s ease-in-out, background 0.4s ease; } /* --- 2. Styles pour l'état visible (communs à mobile et desktop) --- */ .modal.is-visible { opacity: 1; visibility: visible; } /* --- 3. Styles spécifiques pour DESKTOP (écrans > 768px) --- */ @media screen and (min-width: 769px) { .modal { width: 500px; padding: 25px; border-radius: 8px; border: 1px solid black; box-shadow: 0 5px 20px rgba(0,0,0,0.3); /* Centrage parfait */ top: 50%; left: 50%; /* Hauteur maximale pour gérer le contenu long */ max-height: 85vh; display: flex; flex-direction: column; /* Animation : départ légèrement plus petit et décalé */ transform: translate(-50%, -50%) scale(0.95); } .modal.is-visible { /* Animation : arrivée à la taille et position finales */ transform: translate(-50%, -50%) scale(1); } } /* --- 4. Styles spécifiques pour MOBILE (écrans <= 768px) --- */ @media screen and (max-width: 768px) { .modal { /* Géométrie "plein écran" */ width: 100%; height: 100%; top: 0; left: 0; /* Réinitialisation des styles desktop inutiles sur mobile */ border: none; border-radius: 0; box-shadow: none; padding: 20px; /* Permet le défilement si le contenu est trop long */ overflow-y: auto; /* Animation mobile : départ en dehors de l'écran, sur la gauche */ transform: translateX(-100%); } .modal.is-visible { /* Animation mobile : glisse pour occuper l'écran */ transform: translateX(0); } } /* ========================================================================== Contenu de la Modale (bouton de fermeture, etc.) ========================================================================== */ .close-btn { position: absolute; top: 10px; right: 15px; font-size: 1.8rem; font-weight: bold; color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.4); /* Améliore la lisibilité */ line-height: 1; border: none; background: transparent; cursor: pointer; z-index: 10; /* S'assure qu'il est au-dessus du contenu qui défile */ } .modal-content { margin-top: 20px; color: white; } /* Règle spécifique au contenu de la modale sur DESKTOP pour le défilement */ @media screen and (min-width: 769px) { .modal-content { flex: 1; /* Prend toute la hauteur disponible */ overflow-y: auto; /* Active le défilement si nécessaire */ padding-right: 15px; /* Évite que le texte ne colle à la barre de défilement */ } }
app.js
/** * Attend que le contenu de la page (le DOM) soit entièrement chargé avant d'exécuter le script. * C'est une bonne pratique pour s'assurer que tous les éléments HTML existent. */ document.addEventListener('DOMContentLoaded', () => { // --- 1. Sélection des éléments du DOM --- // On récupère tous les éléments HTML avec lesquels on va interagir une seule fois. const modal = document.getElementById('modal'); const overlay = document.getElementById('overlay'); const modalContent = document.getElementById('modal-content'); const closeButton = document.getElementById('modal-close-btn'); const triggerLinks = document.querySelectorAll('.modal-trigger'); // Variable pour mémoriser l'élément qui avait le focus avant l'ouverture de la modale. // C'est crucial pour l'accessibilité. let lastActiveElement; // --- 2. Définition des Fonctions --- /** * Ouvre la modale, charge le contenu et applique la couleur de fond. * @param {HTMLElement} triggerElement - L'élément <a> qui a été cliqué. */ const openModal = async (triggerElement) => { // On récupère l'URL et la couleur depuis les attributs 'data-*' du lien cliqué. const url = triggerElement.dataset.url; const color = triggerElement.dataset.color; if (!url) { console.error("L'attribut 'data-url' est manquant sur le lien.", triggerElement); return; } lastActiveElement = document.activeElement; // On sauvegarde l'élément actif. try { // On charge le contenu depuis l'URL spécifiée. const response = await fetch(url); if (!response.ok) throw new Error(`Le réseau n'a pas répondu correctement`); const content = await response.text(); modalContent.innerHTML = content; // === LA PARTIE NOUVELLE ET IMPORTANTE === // On vérifie si une couleur a été définie dans l'attribut 'data-color'. if (color) { // Si oui, on applique cette couleur à une variable CSS (--modal-background) sur la modale. modal.style.setProperty('--modal-background', color); } else { // Sinon, on supprime la variable pour que le CSS utilise sa couleur par défaut. modal.style.removeProperty('--modal-background'); } // ======================================= // On affiche la modale et l'overlay. modal.classList.add('is-visible'); overlay.classList.add('is-visible'); // On déplace le focus sur le bouton de fermeture (accessibilité). closeButton.focus(); // On ajoute un écouteur pour la touche "Échap". document.addEventListener('keydown', handleEscKey); } catch (error) { modalContent.innerHTML = `<p>Erreur lors du chargement du contenu : ${error.message}</p>`; console.error('Erreur de fetch :', error); } }; /** * Ferme la modale et l'overlay. */ const closeModal = () => { modal.classList.remove('is-visible'); overlay.classList.remove('is-visible'); modalContent.innerHTML = ''; // On vide le contenu. // On redonne le focus à l'élément qui a ouvert la modale (accessibilité). if (lastActiveElement) { lastActiveElement.focus(); } // On retire l'écouteur pour la touche "Échap" pour ne pas l'avoir en permanence. document.removeEventListener('keydown', handleEscKey); }; /** * Fonction qui vérifie si la touche pressée est "Échap" et ferme la modale si c'est le cas. * @param {KeyboardEvent} event - L'événement du clavier. */ const handleEscKey = (event) => { if (event.key === 'Escape') { closeModal(); } }; // --- 3. Initialisation des Écouteurs d'Événements --- // On attache un écouteur de clic à chaque lien qui doit ouvrir la modale. triggerLinks.forEach(link => { link.addEventListener('click', (event) => { event.preventDefault(); // Empêche le lien de naviguer. openModal(link); // On appelle openModal en lui passant le lien cliqué lui-même. }); }); // On attache un écouteur de clic au bouton de fermeture. closeButton.addEventListener('click', closeModal); });
Top comments (0)