
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
<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
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
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
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
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
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
.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);
}

