In questo articolo vedremo come implementare un sistema a tab con i soli JavaScript e CSS.

I tab sono costituiti da una navigazione fatta da link che puntano a specifici elementi nella pagina e dai tab stessi inseriti in un contenitore.

Ciascun tab ha un ID univoco in modo che i link della navigazione possano funzionare correttamente. Solitamente tutti i tab sono inizialmente nascosti ad eccezione del primo.

Quando si clicca su un link della navigazione, il tab corrente viene mostrato e tutti gli altri vengono nascosti. Allo stesso tempo il link corrente viene evidenziato.

I tab devono supportare la navigazione tramite hash. Aprendo quindi la pagina e aggiungendo l'hash #tab-2, dovrà essere mostrato il tab specificato nell'URL, attivando automaticamente la logica della navigazione.

Partiamo dalla seguente struttura HTML:

<div class="tabs">
    <ul class="tabs-nav">
        <li class="tabs-nav-item tabs-nav-item-active">
            <a href="#tab-1">Tab 1</a>
        </li>
        <li class="tabs-nav-item">
            <a href="#tab-2">Tab 2</a>
        </li>
        <li class="tabs-nav-item">
            <a href="#tab-3">Tab 3</a>
        </li>
    </ul>
    <div class="tabs-content">
          <div class="tab tab-active" id="tab-1"></div>
          <div class="tab tab-active" id="tab-2"></div>
          <div class="tab tab-active" id="tab-3"></div>
    </div>
</div>    

Assegniamole gli stili CSS corrispondenti:

.tabs-nav {
    margin: 0;
    padding: 1rem;
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    border: 1px solid #ddd;
}

.tabs-nav-item {
    display: block;
    list-style: none;
    margin-bottom: 1rem;
    text-align: center;
}

.tabs-nav-item a {
    display: block;
}

.tabs-nav-item-active a {
    font-weight: bold;
}

.tabs-content {
    margin: 1rem 0;
}

.tab {
    display: none;
}

.tab-active {
    display: block;
}

@media screen and (min-width: 768px) {

    .tabs-nav {
        padding: 0 0 0.5rem 0;
        border-width: 0 0 1px 0;
        flex-direction: row;
    }

    .tabs-nav-item {
        margin-right: 0.5rem;
        text-align: left;
    }


}

Con JavaScript possiamo creare una classe che contenga tutta la logica della nostra implementazione.

'use strict';

(function () {
    class Tabs {
        constructor() {
            this.tabs = document.querySelectorAll('.tabs');
            if(this.tabs.length > 0) {
                this.init();
            }
        }

        init() {
            this.navigation();
            this.hash();
        }
        
        //...
    }
    window.Tabs = new Tabs();
})();         

Se non esistono tab nella pagina, il codice della classe non verrà mai eseguito.

Il primo metodo da creare è quello che gestisce la navigazione:

//...
navigation() {
            this.tabs.forEach(tab =>  {
                let nav = tab.querySelector('.tabs-nav');
                let navItems = nav.querySelectorAll('a');
                let content = tab.querySelector('.tabs-content');

                navItems.forEach(item => {
                   item.addEventListener('click', evt => {
                       evt.preventDefault();
                       let curItem = item.parentNode;
                       let targetSelector = /^#/.test(item.getAttribute('href')) ? item.getAttribute('href') : '#' + item.getAttribute('href');
                       let targetElement =  document.querySelector(targetSelector);
                       nav.querySelectorAll('.tabs-nav-item').forEach(it => {
                            it.classList.remove('tabs-nav-item-active');
                       });
                       curItem.classList.add('tabs-nav-item-active');
                       content.querySelectorAll('.tab').forEach(tb => {
                          tb.classList.remove('tab-active');
                       });

                       if(targetElement !== null) {
                           targetElement.classList.add('tab-active');
                       }

                   }, false);
                });
            });

        }

Quando si effettua un click su un link della navigazione, viene verificato che l'ID rappresentato dall'hash del link selezioni un elemento esistente. Se esiste, il tab viene mostrato e i suoi fratelli nascosti. Allo stesso modo viene evidenziato il link corrente aggiungendogli una classe CSS che viene però rimossa dagli altri link.

Per abilitare la navigazione mediante hash, dobbiamo sfruttare l'evento hashchange che viene eseguito quando un hash viene aggiunto o modificato nell'URL della pagina corrente.

hash() {
            window.addEventListener('hashchange', () => {
                const link = document.querySelector('a[href="' + location.hash + '"]');
                if(link !== null) {
                    const click = new Event('click');
                    link.dispatchEvent(click);
                }
            }, false);
        }

Se l'hash seleziona un link della navigazione esistente, forziamo l'esecuzione dell'evento click associato a quel link in modo da sfruttare la logica già definita nel precedente metodo.

Demo

JavaScript: tabs