Criar um sistema de separadores em JavaScript e CSS

Criar um sistema de separadores em JavaScript e CSS

Este tutorial mostra-te como criar separadores funcionais com HTML, CSS e JavaScript. A ideia é que te baste juntar os ficheiros CSS e JavaScript à página e aplicar algumas classes às divs encaixadas para tudo funcionar. Vamos também tentar deixá-lo semanticamente correto, acrescentando atributos ARIA aos elementos que o justificam. Não leves essa parte demasiado a sério, porque não tenho a certeza de a ter feito como deve ser. Seja como for, dar sentido ao teu HTML é sempre essencial.

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

HTML dos separadores (tabs)

A estrutura destes separadores pode ser mais ou menos assim. Começamos com uma div com a classe tabs e, lá dentro, duas divs: uma com a classe tab-registers e outra com a classe tab-bodies. O primeiro botão dentro de tab-register está ligado ao primeiro elemento dentro de tab-bodies. Se quisermos, damos a um dos botões a classe active-tab para que passe a ser o separador ativo.

Separadores 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>

JavaScript dos separadores (tabs)

Começamos por obter uma coleção HTML de todos os elementos com a classe tabs e percorremo-la depois de a transformar em lista com Array.from(list_like). O callback do ciclo recebe o próprio elemento e o índice. Vamos precisar do índice para identificar este grupo de separadores de forma única.

Separadores JavaScript

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

A seguir, guardamos em variáveis alguns dos elementos essenciais, ou seja os elementos tabs-registers e tab-bodies.

Separadores JavaScript

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

Continuando, vamos buscar o registo ativo mas, como pode estar indefinido, nesse caso fixamo-lo no primeiro botão. Em qualquer dos casos, acrescentamos a classe active-tab ao elemento.

Separadores JavaScript

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

Feito isso, chamamos a função changeBody() que, como o nome diz, esconde todos os corpos exceto o que corresponde ao registo/separador atual. Chamamos também esta função sempre que o separador muda.

Agora percorremos igualmente todos os elementos que estão diretamente dentro do contentor de registos. São os botões/separadores. Definimos depois o atributo aria-controls, que indica que outro elemento é controlado por este. Também temos de definir um id para o corpo correspondente. Usamos o id do separador e o índice do botão para construir um id único.

A seguir, acrescentamos um listener de eventos ao botão, retiramos a classe de separador ativo do registo atual e substituímos o conteúdo da variável pelo novo registo atual, ao qual temos igualmente de acrescentar a classe de separador ativo. Por fim, chamamos a função changeBody().

Separadores 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)
  })
})

Passemos agora à função changeBody(). Nela, voltamos a percorrer os registos filhos, obtendo também o índice de cada iteração. Depois, se o corpo desse índice existir, decidimos se a sua propriedade de display fica em block ou em none conforme o registo atual. Fazemos esta verificação porque pode haver mais registos do que corpos. Por fim, definimos ainda o atributo aria-expanded do botão, que diz ao utilizador se o conteúdo desse painel, ou do botão que leva a um painel, está expandido ou não.

Separadores 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')
    })
}

CSS dos separadores

Separadores 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 Programador web desde 2010, criador da Gekkode e do Email Impact. Cada artigo é testado num projeto real antes de ser publicado. Contacto
Newsletter

Os novos testes, tutoriais e projetos, por e-mail.

Testes reproduzíveis, código versionado, resultados datados. Nunca spam.