
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
<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
Array.from(document.querySelectorAll('.tabs')).forEach((tab_container, TabID) => {
...
})Poi salviamo in variabili gli elementi essenziali, cioè tabs-registers e tab-bodies.
Tab 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
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
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
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
.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);
}

