Modale Accessibile con Focus Trap

by murgiaMarco

HTML

<button id="openModal">Apri Modale</button>

    <div class="overlay" id="overlay">
        <div class="modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle" aria-describedby="modalDesc">
            <h2 id="modalTitle">Titolo Modale</h2>
            <p id="modalDesc">Questa è una modale accessibile con trap del focus.</p>
            <button id="closeModal">Chiudi</button>
        </div>
    </div>

CSS

body {
        font-family: Arial, sans-serif;
        margin: 0;
        padding: 20px;
    }
    .overlay {
        position: fixed;
        top: 0; left: 0;
        width: 100%; height: 100%;
        background: rgba(0,0,0,0.5);
        display: none;
        align-items: center;
        justify-content: center;
    }
    .modal {
        background: #fff;
        padding: 20px;
        border-radius: 8px;
        width: 300px;
        max-width: 90%;
        box-shadow: 0 2px 8px rgba(0,0,0,0.3);
    }
    .modal h2 {
        margin-top: 0;
    }
    .modal button {
        margin-top: 15px;
        padding: 10px;
        background: #0078d7;
        color: #fff;
        border: none;
        border-radius: 4px;
        cursor: pointer;
    }
    .modal button:hover {
        background: #005fa3;
    }

JavaScript

const openBtn = document.getElementById('openModal');
    const closeBtn = document.getElementById('closeModal');
    const overlay = document.getElementById('overlay');
    const modal = overlay.querySelector('.modal');

    let lastFocusedElement = null;

    // Funzione per aprire la modale
    openBtn.addEventListener('click', () => {
        lastFocusedElement = document.activeElement;
        overlay.style.display = 'flex';
        modal.setAttribute('tabindex', '-1');
        modal.focus();
        trapFocus(modal);
    });

    // Funzione per chiudere la modale
    function closeModal() {
        overlay.style.display = 'none';
        lastFocusedElement.focus();
    }

    closeBtn.addEventListener('click', closeModal);

    // Chiudi con ESC
    document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape' && overlay.style.display === 'flex') {
            closeModal();
        }
    });

    // Trap del focus
    function trapFocus(element) {
        const focusableSelectors = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
        const focusableElements = element.querySelectorAll(focusableSelectors);
        const firstEl = focusableElements[0];
        const lastEl = focusableElements[focusableElements.length - 1];

        document.addEventListener('keydown', function(e) {
            if (overlay.style.display !== 'flex') return;

            if (e.key === 'Tab') {
                if (e.shiftKey) {
                    // SHIFT + TAB
                    if (document.activeElement === firstEl) {
                        e.preventDefault();
                        lastEl.focus();
                    }
                } else {
                    // TAB normale
                    if (document.activeElement === lastEl) {
                        e.preventDefault();
                        firstEl.focus();
                    }
                }
           ...