Novo Livro!
Categorias
Projetos
CSS

Guia da propriedade CSS pointer-events

Guia da propriedade CSS pointer-events

Overlay que bloqueia o clique no que está atrás, submenu invisível que continua interceptando o mouse: os dois problemas se resolvem com uma linha de pointer-events.

Só que a propriedade não desabilita eventos: ela muda qual elemento é escolhido como alvo. Entender essa diferença explica praticamente todo o comportamento dela.

A propriedade pointer-events controla se um elemento pode se tornar o alvo de eventos de ponteiro, como cliques, estados de hover e outros eventos baseados em ponteiro. Em outras palavras, ela permite decidir se o navegador deve tratar um elemento como interativo quando o ponteiro está sobre ele.

.no-pointer-events {
pointer-events: none;
}

Para entender como a propriedade funciona, ajuda saber o que o navegador faz antes de disparar um evento de ponteiro. Primeiro, ele precisa determinar qual elemento está sob o ponteiro. Esse processo é conhecido como hit-testing.

Normalmente, o navegador escolhe o elemento mais ao topo sob o ponteiro. Mas, se esse elemento tiver pointer-events definido como none, o navegador o ignora e continua procurando o próximo elemento elegível abaixo dele.

Uma vez que se pensa em pointer-events dessa forma, a maior parte do comportamento dela começa a fazer sentido. Em vez de desabilitar eventos, ela simplesmente muda qual elemento (ou, no caso de SVG, qual parte de um elemento) se torna o alvo do evento.

Sintaxe

pointer-events: auto | bounding-box | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | none;
  • Valor inicial: auto
  • Aplica-se a: todos os elementos. Em SVG, aplica-se a elementos container, elementos gráficos e ao próprio elemento.
  • Herdada: sim
  • Valor computado: a palavra-chave especificada
  • Tipo de animação: discreto

Valores

pointer-events: auto;
pointer-events: none;
/* Valores de SVG */
pointer-events: visiblePainted;
pointer-events: visibleFill;
pointer-events: visibleStroke;
pointer-events: visible;
pointer-events: painted;
pointer-events: fill;
pointer-events: stroke;
pointer-events: bounding-box;
pointer-events: all;
/* Valores globais */
pointer-events: inherit;
pointer-events: initial;
pointer-events: revert;
pointer-events: revert-layer;
pointer-events: unset;

Além dos valores globais padrão de CSS mostrados acima, a pointer-events define onze palavras-chave.

Você vai usar auto e none tanto com elementos HTML quanto SVG. As outras nove são exclusivas de SVG e oferecem controle mais fino sobre quais partes de um gráfico podem receber eventos de ponteiro.

  • auto: o valor padrão. O elemento se comporta normalmente e pode receber eventos de ponteiro. Em SVG, funciona igual a visiblePainted.
  • none: o elemento em si não pode se tornar alvo de eventos de ponteiro, o que significa que ele não pode ser clicado nem receber hover. Em vez dele, o navegador mira no que estiver embaixo.

Valores exclusivos de SVG

  • visiblePainted: o elemento só recebe eventos de ponteiro quando está visível (visibility: visible) e o ponteiro está sobre uma parte pintada do gráfico. Ou seja, sobre uma área preenchida (fill diferente de none) ou sobre uma borda traçada (stroke diferente de none).
  • visibleFill: o elemento só recebe eventos de ponteiro quando está visível e o ponteiro está sobre o preenchimento dele, independentemente do valor da propriedade fill, que pode até estar como none.
  • visibleStroke: o elemento só recebe eventos de ponteiro quando está visível e o ponteiro está sobre o traço dele, independentemente do valor da propriedade stroke.
  • visible: o elemento só recebe eventos de ponteiro quando está visível e o ponteiro está sobre o preenchimento ou o traço, independentemente dos valores das propriedades fill e stroke.
  • painted: o elemento só recebe eventos de ponteiro quando o ponteiro está sobre uma parte pintada do gráfico (preenchimento ou traço), independentemente do valor da propriedade visibility.
  • fill: o elemento só recebe eventos de ponteiro quando o ponteiro está sobre o preenchimento dele, independentemente dos valores das propriedades fill e visibility.
  • stroke: o elemento só recebe eventos de ponteiro quando o ponteiro está sobre o traço dele, independentemente dos valores das propriedades stroke e visibility.
  • bounding-box: o elemento recebe eventos de ponteiro em qualquer ponto dentro da bounding box dele (o menor retângulo que o cerca por completo), independentemente do formato, mesmo que partes dessa área não estejam pintadas.
  • all: o elemento recebe eventos de ponteiro quando o ponteiro está sobre o preenchimento ou o traço dele, independentemente dos valores das propriedades fill, stroke ou visibility.

Filhos podem voltar a participar

Algo fácil de deixar passar é que pointer-events é uma propriedade herdada. Definir pointer-events como none em um pai significa que os filhos também herdam esse valor.

Só que qualquer filho pode sobrescrever o valor herdado, voltando pointer-events para auto (ou outro valor válido).

.parent {
pointer-events: none;
}
.child {
pointer-events: auto;
}

Neste exemplo, o pai ignora eventos de ponteiro, mas o filho ainda pode se tornar alvo deles.

Um caso de uso comum é um modal. Você pode usar um container de página inteira para centralizar o modal, mas esse container também cobre toda a viewport. Sem mudar o valor de pointer-events dele, ele impede que eventos de ponteiro cheguem aos elementos abaixo.

Definir pointer-events como none no container resolve isso. Mas, como a propriedade é herdada, também é preciso restaurar o próprio modal com pointer-events em auto.

Na demonstração a seguir, quando a propriedade pointer-events está especificada como none, é possível interagir com os botões de fundo mesmo estando eles atrás do overlay.

A propagação de eventos continua funcionando

A propriedade pointer-events só determina qual elemento se torna o alvo do evento. Ela não muda como o evento trafega pelo DOM depois disso.

Por exemplo: se um filho com pointer-events: auto é clicado dentro de um pai com pointer-events: none, o filho ainda se torna o event.target. A partir dali, o evento segue as fases normais de captura e bubbling, então os listeners anexados ao pai continuam rodando.

Em outras palavras, pointer-events afeta a seleção do alvo, não a propagação do evento.

Na demonstração a seguir, dá para ver como o pai com pointer-events: none ainda pode receber click, pointerenter e pointerleave quando você clica ou move o ponteiro para dentro ou para fora do filho interativo.

Ela não desabilita um elemento

A propriedade pointer-events só impede que o elemento se torne alvo de eventos de ponteiro. Portanto, o elemento ainda pode receber foco de teclado com a tecla Tab, e o usuário pode continuar interagindo com ele pelo teclado, se ele for focável por outros meios.

Se você precisa desabilitar um controle de formulário nativo, use o atributo disabled. E, se o objetivo é tornar uma seção inteira da página completamente não interativa (incluindo entrada por ponteiro, foco de teclado e a árvore de acessibilidade), o atributo inert é a melhor escolha.

Essa distinção importa: esconder algo do mouse sem esconder do teclado é uma armadilha clássica de acessibilidade em CSS.

Ela não impede a seleção de texto

Definir a propriedade pointer-events como none não impede que o usuário selecione texto. Ele ainda pode selecioná-lo pressionando Ctrl/Cmd + A no teclado, por exemplo.

Isso acontece porque a seleção de texto não é determinada por um elemento poder ou não se tornar alvo de eventos de ponteiro.

Se o objetivo é impedir que o texto seja selecionado, use a propriedade user-select.

.avoid-user-selection {
user-select: none;
}

Tente selecionar o texto de cada bloco abaixo:

Demonstração

Na construção de um menu de navegação, um padrão comum é esconder um submenu definindo a opacity dele em 0 e depois torná-lo visível quando o usuário passa o mouse sobre o item pai. O problema é que o submenu continua ali, então ele ainda pode receber eventos de ponteiro mesmo sem estar visível.

Abaixo, dá para ver dois menus idênticos. Um esconde o submenu usando apenas opacidade, e o outro usa também pointer-events. Passe o mouse sobre a área do submenu invisível e tente interagir com o conteúdo atrás dela, para ver como a pointer-events evita que elementos invisíveis fiquem no caminho.

Também para entender melhor como cada valor de SVG dessa propriedade funciona, escolha um deles no dropdown e mova o ponteiro pelo anel: sobre a faixa preenchida, dentro do centro vazado e sobre os cantos vazios da bounding box tracejada. Note como a área interativa muda a cada valor.

Suporte dos navegadores

Os valores auto e none funcionam em todos os navegadores em uso, incluindo versões bem antigas. Os valores exclusivos de SVG têm suporte irregular, com o bounding-box sendo o mais recente e o menos difundido. Vale conferir a tabela de suporte no Can I Use antes de depender de um valor específico de SVG.

Propriedades relacionadas

A parceira mais próxima é a user-select, que resolve o problema que a pointer-events não resolve: controlar a seleção de texto. E, para casos de menu, vale ver as 10 dicas para menus responsivos.