Não focaremos tanto na acessibilidade neste tutorial, então explorar como tornar esse componente mais acessível seria um próximo passo válido.
Nosso componente de acordeão
Aqui está o que vamos criar (clique em um painel para testar o comportamento):
1. Comece com a marcação da página
Dentro de um contêiner, colocaremos uma lista de painéis.
Cada painel terá um título e conteúdo. Dentro do título, adicionaremos um Fechar botão de onde podemos fechar o painel ativo.
Aqui está a estrutura necessária:
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
Estado inicial do acordeão/itens ativos
Por padrão, todos os painéis serão recolhidos.
Para evitar esse comportamento, temos que atribuir o active
classe para um ou mais painéis como este:
1 |
|
2 |
|
3 |
|
Vários painéis abertos
Também há a opção de ter mais de um painel aberto simultaneamente sem que um se feche quando o outro estiver aberto. Para habilitar isso, devemos adicionar o data-multiple="true"
atribuir ao wrapper do acordeão assim:
1 |
|
2. Adicione o CSS
Vamos agora nos concentrar nos estilos principais — a maioria dos outros estilos não tem nada de especial, então vamos deixá-los por enquanto:
- Para fazer os painéis se sobreporem e criar um layout de acordeão diferente em comparação aos padrões, daremos a eles uma margem superior negativa e um preenchimento inferior igual. Apenas para o primeiro e o último itens, cancelaremos a margem superior e o preenchimento inferior, respectivamente.
- Para ocultar o conteúdo de cada painel, daremos a eles
height: 0
eoverflow: hidden
. Então, como veremos mais tarde, por meio do JavaScript, recalcularemos sua altura e as revelaremos suavemente. Apenas observe que também usaremosheight: 0 !important
para redefinir a altura para 0 e substituir os estilos JavaScript de um painel ativo anteriormente. - Para abrir o modal, toda a área do painel será clicável. Para deixar claro, atribuiremos
cursor: pointer
para todos os painéis. Ao contrário, quando um painel está aberto, podemos fechá-lo somente através do botão fechar. Neste momento, somente este botão terácursor: pointer
enquanto o painel terácursor: default
.
Aqui está uma parte dos estilos necessários:
1 |
/*CUSTOM STYLES HERE*/
|
2 |
|
3 |
.accordion-wrapper li { |
4 |
padding: 0 20px 100px; |
5 |
cursor: pointer; |
6 |
border-top-left-radius: var(--accordion-radius); |
7 |
border-top-right-radius: var(--accordion-radius); |
8 |
background: var(--accordion-bg-color); |
9 |
transition: all 0.2s ease-out; |
10 |
}
|
11 |
|
12 |
.accordion-wrapper li:not(:first-child) { |
13 |
margin-top: -100px; |
14 |
border-top: 2px solid var(--light-cyan); |
15 |
}
|
16 |
|
17 |
.accordion-wrapper li:nth-last-child(2), |
18 |
.accordion-wrapper li:last-child { |
19 |
border-bottom-left-radius: var(--accordion-radius); |
20 |
border-bottom-right-radius: var(--accordion-radius); |
21 |
}
|
22 |
|
23 |
.accordion-wrapper li:last-child { |
24 |
padding-bottom: 0; |
25 |
}
|
26 |
|
27 |
.accordion-wrapper:not([data-multiple="true"]) li.active { |
28 |
border-top-color: var(--accordion-active-bg-color); |
29 |
}
|
30 |
|
31 |
.accordion-wrapper li.active { |
32 |
cursor: default; |
33 |
color: var(--white); |
34 |
background: var(--accordion-active-bg-color); |
35 |
}
|
36 |
|
37 |
.accordion-wrapper li:not(.active) .accordion-content { |
38 |
height: 0 !important; |
39 |
}
|
40 |
|
41 |
.accordion-wrapper .accordion-content { |
42 |
height: 0; |
43 |
overflow: hidden; |
44 |
transition: height 0.3s; |
45 |
}
|
46 |
|
47 |
.accordion-wrapper .inner { |
48 |
padding-bottom: 40px; |
49 |
}
|
50 |
|
51 |
@media (min-width: 700px) { |
52 |
.accordion-wrapper li { |
53 |
padding-left: 60px; |
54 |
padding-right: 60px; |
55 |
}
|
56 |
|
57 |
.accordion-wrapper .inner { |
58 |
max-width: 85%; |
59 |
}
|
60 |
}
|
3. Adicione o JavaScript
A maneira como animaremos cada painel e obteremos um efeito de slide semelhante ao do jQuery slideToggle()
a função é aproveitar a scrollHeight
propriedade.
Esta propriedade mede a altura do conteúdo de um elemento, incluindo conteúdo não visível na tela devido a estouro. No nosso caso, precisaremos calcular esse valor para o .accordion-content
elementos que têm height: 0
e overflow: hidden
por padrão.
Quando DOM estiver pronto
Como primeira ação, quando o DOM estiver pronto, verificaremos se há algum painel ativo e, em caso positivo, definiremos a altura para o mesmo. .accordion-content
elemento de cada painel ativo igual ao seu scrollHeight
valor da propriedade.
Aqui está o código JavaScript relacionado:
1 |
const activeItems = accordionWrapper.querySelectorAll("li.active"); |
2 |
|
3 |
if (activeItems) { |
4 |
activeItems.forEach(function (item) { |
5 |
const content = item.querySelector(".accordion-content"); |
6 |
content.style.height = `${content.scrollHeight}px`; |
7 |
});
|
8 |
}
|
Alternar painéis de acordeão
Em seguida, cada vez que clicarmos em um painel, faremos o seguinte:
- Verifique se clicamos no botão fechar. Se isso acontecer e o painel estiver aberto, fecharemos removendo o
active
classe e ignorando todos os próximos passos. - Verifique se definimos a opção de ter vários painéis abertos juntos. Se não for o caso e houver um painel ativo, nós o fecharemos.
- Adicione o
active
classe para esse painel. - Defina a altura para o
.accordion-content
elemento deste painel igual ao seuscrollHeight
valor da propriedade.
Aqui está o código JavaScript que implementa todo esse comportamento:
1 |
const accordionWrapper = document.querySelector(".accordion-wrapper"); |
2 |
const items = accordionWrapper.querySelectorAll("li"); |
3 |
const multiple_open = accordionWrapper.dataset.multiple; |
4 |
const ACTIVE_CLASS = "active"; |
5 |
|
6 |
items.forEach(function (item) { |
7 |
item.addEventListener("click", function (e) { |
8 |
// 1
|
9 |
const target = e.target; |
10 |
if ( |
11 |
(target.tagName.toLowerCase() === "button" || target.closest("button")) && |
12 |
item.classList.contains(ACTIVE_CLASS) |
13 |
) { |
14 |
item.classList.remove(ACTIVE_CLASS); |
15 |
return; |
16 |
}
|
17 |
|
18 |
// 2
|
19 |
if ( |
20 |
"true" !== multiple_open && |
21 |
document.querySelector(".accordion-wrapper li.active") |
22 |
) { |
23 |
document
|
24 |
.querySelector(".accordion-wrapper li.active") |
25 |
.classList.remove(ACTIVE_CLASS); |
26 |
}
|
27 |
|
28 |
// 3
|
29 |
item.classList.add(ACTIVE_CLASS); |
30 |
|
31 |
// 4
|
32 |
const content = item.querySelector(".accordion-content"); |
33 |
content.style.height = `${content.scrollHeight}px`; |
34 |
});
|
35 |
});
|
Conclusão
Pronto! Espero que você tenha gostado do acordeão JavaScript que construímos e tenha aprendido uma ou duas coisas novas.
Antes de encerrar, vamos relembrar nossa principal criação de hoje:
Como sempre, muito obrigado pela leitura!