Creare un sistema di tab accessibile in JavaScript e CSS

Creare un sistema di tab accessibile in JavaScript e CSS

Questo tutorial ti guida nella creazione di tab funzionanti con HTML, CSS e JavaScript. L’idea è che ti basti aggiungere i file CSS e JavaScript alla pagina e applicare alcune classi ai div annidati perché tutto funzioni. Proveremo anche a renderlo semanticamente corretto, aggiungendo attributi ARIA agli elementi giusti. Non prenderla però troppo alla lettera: non sono certo di averlo fatto in modo impeccabile. In ogni caso, dare un senso al tuo HTML resta essenziale.

See the Pen
Onglets HTLM/CSS/JS
by Damien Flandrin (@dam62500)
on CodePen.0

L’HTML dei tab

Una struttura possibile per questi tab è la seguente. Prima un div con la classe tabs, poi due div al suo interno, uno con la classe tab-registers e uno con la classe tab-bodies. Il primo bottone dentro tab-register è collegato al primo elemento dentro tab-bodies. Volendo, puoi dare a uno dei bottoni la classe active-tab perché diventi il tab attivo.

Tab HTML

html
<div class="tabs">
    <div class="tab-registers">
        <button class="active-tab">Tab 1</button>
        <button>Tab 2</button>
        <button>Tab 2</button>
    </div>
    <div class="tab-bodies">
        <div style="display:block;">
            Contenu Tab 1
        </div>
        <div style="display:none;">
            Contenu Tab 2
        </div>
        <div style="display:none;">
            Contenu Tab 3
        </div>
    </div>
</div>

Il JavaScript dei tab

Si parte recuperando una HTMLCollection di tutti gli elementi con la classe tabs, che percorriamo dopo averla trasformata in lista con Array.from(list_like). La callback del ciclo riceve l’elemento stesso e l’indice. L’indice ci serve per identificare in modo univoco questo gruppo di tab.

Tab JavaScript

javascript
Array.from(document.querySelectorAll('.tabs')).forEach((tab_container, TabID) => {
    ...
})

Poi salviamo in variabili gli elementi essenziali, cioè tabs-registers e tab-bodies.

Tab JavaScript

javascript
const registers = tab_container.querySelector('.tab-registers');
const bodies = tab_container.querySelector('.tab-bodies');

Andiamo avanti recuperando il registro attivo: siccome può essere undefined, in quel caso lo fissiamo sul primo bottone. In ogni caso aggiungiamo la classe active-tab all’elemento.

Tab JavaScript

javascript
let activeRegister = registers.querySelector('.active-tab');
activeRegister = activeRegister ? activeRegister : registers.children[0]
activeRegister.classList.add('active-tab')

Dopodiché chiamiamo la funzione changeBody() che, come dice il nome, nasconde tutti i corpi tranne quello corrispondente al registro/tab corrente. Chiamiamo questa funzione anche a ogni cambio di tab.

Adesso percorriamo anche tutti gli elementi che stanno direttamente dentro il contenitore dei registri: sono i bottoni/tab. Impostiamo poi l’attributo aria-controls, che indica quale altro elemento viene controllato da questo. Dobbiamo anche definire un id per il corpo corrispondente: usiamo l’id del tab e l’indice del bottone per costruire un id univoco.

Poi aggiungiamo un event listener al bottone, togliamo la classe di tab attivo dal registro corrente e sostituiamo il contenuto della variabile con il nuovo registro corrente, a cui aggiungiamo a sua volta la classe di tab attivo. Infine chiamiamo la funzione changeBody().

Tab JavaScript

javascript
Array.from(registers.children).forEach((el, i) => {
  el.setAttribute('aria-controls', `${TabID}_${i}`)
  bodies.children[i]?.setAttribute('id', `${TabID}_${i}`)

el.addEventListener('click', (ev) => {
    let activeRegister = registers.querySelector('.active-tab');
    activeRegister.classList.remove('active-tab')
    activeRegister = el;
    activeRegister.classList.add('active-tab')
    changeBody(registers, bodies, activeRegister)
  })
})

Passiamo ora alla funzione changeBody(). Al suo interno percorriamo di nuovo i registri figli, recuperando l’indice a ogni iterazione. Se il corpo a quell’indice esiste, decidiamo se la sua proprietà display vale block o none in base al registro corrente. Facciamo questo controllo perché i registri possono essere più numerosi dei corpi. Poi impostiamo anche l’attributo aria-expanded sul bottone, che dice all’utente se il contenuto di quel pannello, o del bottone che porta a un pannello, è espanso o no.

Tab JavaScript

javascript
function changeBody() {
    Array.from(registers.children).forEach((el, i) => {
        if (bodies.children[i]) {
            bodies.children[i].style.display = el == activeRegister ? 'block' : 'none'
        }

el.setAttribute('aria-expanded', el == activeRegister ? 'true' : 'false')
    })
}

Il CSS dei tab

Tab CSS

css
.tabs {
    font-family: 'Lucida Sans', sans-serif;
    font-size: 20px;
}

.tabs .tab-registers {
    display: flex;
    background-color: RGB(255, 255, 255);
}

.tabs button {
    padding: 0.5em;
    background-color: RGB(255, 255, 255);
    border: none;
    font: inherit;
}

.tabs .tab-registers button:hover {
    cursor: pointer;
}

.tabs .tab-bodies {
    padding: 0.5em;
    background-color: RGB(235, 235, 235);
    flex-grow: 1;
    overflow-y: auto;
}

.tabs button.active-tab {
    background-color: rgb(235, 235, 235);
}

CSSHTMLJavaScript

Damien Flandrin Sviluppatore web dal 2010, creatore di Gekkode e di Email Impact. Ogni articolo è testato su un progetto reale prima della pubblicazione. Contatti
Newsletter

I nuovi test, tutorial e progetti, via e-mail.

Test riproducibili, codice versionato, risultati datati. Mai spam.