Âncoras versus botões
Introdução
Por muito tempo desenvolvedores web (inclusive eu) usamos elementos <a>
, <div>
, <span>
e uma série de outros exceto o elemento <button>
para criar componentes interativos clicáveis em uma página. Algo semelhante ao mostrado a seguir.
<a href="#" role="button" onclick="openMenuModal();">Navegação</a>
O atributo href
definido para o elemento âncora recebe um valor tal que quando o usuário clica o link não acontece a esperada ação de navegação típica de quando se clica um link. Usa-se JavaScript para alterar a ação padrão do evento clique. Mas, existe um elemento HTML apropriado para exercer aquele comportamento que foi simulado com uso da JavaScript. Trata-se do elemento <button>
ou mais especificamente o elemento <button type="button">
.
<button type="button" onclick="openMenuModal();">Navegação</button>
Apesar disto o elemento âncora vem sendo amplamente usado para disparar interações na página, e na maioria dos casos, de forma incorreta. Contudo, convém notar que o botão não é a solução para tudo e eu acredito que em alguns casos o uso da âncora é mais apropriado. Para entendermos o quando e o porquê vamos começar estabelecendo as diferenças entre âncoras e botões.
Diferenças entre âncoras e botões
Visualmente os elementos <a>
e <button type="button">
podem ser exatamente iguais, pois para isso basta que sejam estilizados apropriadamente, porém semanticamente eles são diferentes e isso para usuários com necessidades especiais particularmente aqueles com restrições de exercer ações de apontar e clicar, tais como aqueles incapazes de usar o mouse, poderá ser desastroso.
Elemento âncora
O elemento <a>
representa um hyperlink cujo destino é uma página ou uma seção dentro da própria página. O rótulo deste elemento é o seu conteúdo que poderá ser qualquer um, desde uma imagem até um elemento adicional não interativo tais como elementos <div>
, <p>
, etc.
Uma vez que o elemento <a>
relaciona-se a um link, para ele estão disponíveis as pseudo-classes CSS :link
e :visited
. E também as pseudo-classes :hover
, :active
e :focus
relacionadas à interação do usuário.
Ao se navegar com uso do teclado os elementos âncora podem ser "clicados" quando se pressiona a tecla Enter
. Os leitores de tela, quando um link é clicado, esperam que o usuário navegue para uma página externa ou para uma seção na mesma página.
Elemento botão
O elemento <button>
, ou mais especificamente o elemento<button type="button">
não faz nada. Ao contrário de outros tipos de botão, tal como o <button type="reset">
que reseta (limpa) os campos de um formulário o <button type="button">
não tem um comportamento padrão e em consequência não desencadeia nenhum tipo de ação.
Pelo fato de que nenhum link está associado ao botão, somente as pseudo-classes CSS de ação do usuário (:hover
, :active
e :focus
) estão disponíveis para este elemento. Assim botões podem ser estilizados facilmente como se elementos âncora fossem.
Na navegação com uso de teclado, botões podem ser "clicados" ao se pressionar a tecla Enter
ou a tecla para inserção de espaço, sendo esta a mais comumente usada. Para leitores de tela a expectativa de ação quando o usuário clica um botão na página é de que seja acionado um script JavaScript de interação. Alguns leitores de tela anunciam o elemento como sendo um botão dizem ao usuário: "click".
Quais são os problemas?
Problema com botões
Em primeiro lugar quero repetir que se a sua marcação se parece com a mostrada a seguir ...
<a href="#" role="button" onclick="openMenuModal();">Navegação</a>
... você deveria usar um botão.
Contudo, em alguns casos, usar um botão isoladamente não é necessariamente a solução mais adequada.
O elemento <button type="button">
, como já dissemos, não faz nada. É preciso usar JavaScript para adicionar funcionalidade ao botão. Por outro lado, se JavaScript estiver desabilitado no dispositivo do usuário, ele não terá como interagir com o botão que no nosso exemplo seria acessar o menu de navegação.
Embora tenhamos feito tudo certo para leitores de tela usando uma marcação semântica bloqueamos acesso aos usuários com JavaScript desabilitado.
Problema com âncoras
A solução para estes casos é usar o elemento âncora modificando-o de duas formas distintas conforme descritas a seguir.
- Disponibilizar uma alternativa expressiva no atributo
href
; - Alterar a semântica do elemento âncora de modo a que ele se pareça e se comporte como um botão para leitores de tela.
Observe a marcação a seguir que esclarece o que foi dito.
<a href="#navigation-alternative" role="button" onclick="openMenu();">Navegação</a>
Assim, se o dispositivo do usuário está com JavaScript desabilitado o acesso ao conteúdo da navegão será possível. Adicionalmente, uma vez que o botão é "clicável" com uso da tecla de espaço é possível atrelar o evento click à âncora-botão como mostrado a seguir.
var bindSpaceKey = function(anchor) {
anchor.addEventListener('keyup', function(event) {
if (event.keyCode == 32) { anchor.click(); }
})
}
var anchorButtons = document.querySelectorAll('a[role="button"]');
for ( var i = 0; i < anchorButtons.length; i++ ) {
bindSpaceKey( anchorButtons[i] );
}
Na minha opinião, embora esta solução seja melhor do que usar um botão, ela não é perfeita. Primeiro: se JavaScript não está habilitado o elemento âncora deverá ser semanticamente um link e não um botão. Mas, uma vez que definimos role="button" para a âncora, ela será sempre um botão para os leitores de tela, mesmo nos casos em que não o é.
Segundo: ainda que tenhamos definido role="button" para a âncora eu não estou inteiramente convencida de que isso faça da âncora um elemento exatamente igual a um botão. Ainda que os leitores de tela e demais agentes de usuário devam tratá-la como um botão, não está garantido que o farão (link abre em nova janela).
Qual é a solução
Estamos diante de um dilema.
O elemento
<button>
é semanticamente correto, mas não atende o quesito melhoria progressiva (progressive enhancement) e, ao contrário, o elemento<a>
atende o quesito melhoria progressiva, mas é semanticamente impróprio.
Assim, a única solução que eu vejo é alterar o elemento usado baseado nas circunstâncias. Em nome da consistência e da semântica, vamos usar JavaScript para vincular a tecla de espaço ao evento click em <a role="button">
. Quando Javascript estiver desabilitado o elemento permanece como um link regular e semanticamente correto. Mas, quando Javascript estiver habilitado o elemento âncora é trocado por um elemento botão.
Na marcação HTML usamos um link regular sem modificações. Observe a seguir a solução proposta.
<a href="#navigation-alternative" class="anchorButton">Navegação</a>
Se JavaScript estiver habilitado usamos esta linguagem para trocar todas as âncoras por botões.
var changeAnchorToButton = function(anchor) {
// Criar elemento botão com os mesmos conteúdos e atributos das âncoras
var button = document.createElement('button');
button.setAttribute('type', 'button');
button.innerHTML = anchor.innerHTML;
button.id = anchor.id;
for ( var i = 0; i < anchor.classList.length; i++ ) {
button.classList.add(anchor.classList[i]);
}
// Substitui âncoras por botões
var anchorParent = anchor.parentNode;
anchorParent.replaceChild(button, anchor);
}
var anchorButtons = document.getElementsByClassName('anchorButton');
for ( var i = 0; i < anchorButtons.length; i++ ) {
changeAnchorToButton( anchorButtons[i] );
}
Podemos estilizar o seletor .anchorButton
de modo que tanto <a>
quanto <button>
sejam visualmente iguais como mostrado a seguir.
.anchorButton {
/* Resets */
font-family: sans-serif;
font-size: 16px;
color: #000;
border: 1px solid #000;
border-radius: 0;
background-color: transparent;
margin: 0;
padding: 0;
height: auto;
width: auto;
cursor: pointer;
/* Mais estilos... */
}
a.anchorButton {
/* Estilos para elementos <a> */
text-decoration: none;
}
button.anchorButton {
/* Estilos para elementos <button> */
-webkit-appearance: none;
-moz-appearance: none;
}
Embora esta solução seja radical, foi a única que eu consegui desenvolver atendendo os critérios de consistência visual, semântica e acessibilidade. E, a estilização conveniente do seletor .anchorButton
não afetará a consistência visual da página como um todo.