Back

Como Impedir que uma Página Role Enquanto um Diálogo Está Aberto

Como Impedir que uma Página Role Enquanto um Diálogo Está Aberto

Abrir um diálogo modal deve focar a atenção do usuário nesse diálogo—não permitir que ele role a página por trás. No entanto, prevenir a rolagem de fundo permanece surpreendentemente difícil, especialmente quando você precisa de um comportamento confiável de bloqueio de rolagem no iOS Safari.

O elemento <dialog> com showModal() lida automaticamente com o bloqueio de interação. O navegador marca tudo fora do diálogo como inert, impedindo cliques e navegação por teclado. Mas a prevenção de rolagem? Esse é um problema separado que a plataforma não resolve completamente.

Este artigo aborda as principais abordagens para implementações de diálogo modal com bloqueio de rolagem, suas compensações e por que nenhuma solução única funciona perfeitamente em todos os lugares.

Pontos-Chave

  • O método showModal() bloqueia a interação, mas não impede a rolagem de fundo
  • O CSS overflow: hidden funciona em desktop, mas falha no iOS Safari
  • A técnica de body fixo com restauração da posição de rolagem fornece a solução cross-browser mais confiável
  • Use overscroll-behavior: contain junto com outros métodos para prevenir o encadeamento de rolagem
  • Sempre mantenha seu diálogo rolável com max-height e overflow-y: auto para acessibilidade

O Que showModal() Faz e Não Faz

Quando você chama dialog.showModal(), o navegador:

  • Exibe o diálogo na camada superior (top layer)
  • Cria um pseudo-elemento ::backdrop
  • Torna o conteúdo de fundo inerte (bloqueia interação por ponteiro e teclado)
  • Captura o foco dentro do diálogo

O que ele não faz: impedir de forma confiável a rolagem de fundo em todos os navegadores e dispositivos. Em algumas plataformas—principalmente mobile—os usuários ainda podem rolar a página usando gestos de toque ou rodas de rolagem.

A Abordagem CSS Simples: overflow: hidden

A solução mais comum usa o seletor :has() para detectar um diálogo aberto:

body:has(dialog[open]) {
  overflow: hidden;
}

Isso funciona na maioria dos navegadores desktop. A página se torna não rolável enquanto o diálogo está aberto.

O problema: Esta abordagem não é confiável no iOS Safari. A rolagem por toque frequentemente ignora overflow: hidden no body, permitindo que os usuários rolem o fundo de qualquer forma. Se seu público inclui usuários do Safari mobile, você precisará de algo mais robusto.

A Técnica de Body Fixo para Confiabilidade Cross-Browser

A solução cross-browser mais confiável—incluindo bloqueio de rolagem no iOS Safari—usa JavaScript para fixar o body no lugar:

let scrollPosition = 0;

function lockScroll() {
  scrollPosition = window.scrollY;
  document.body.style.position = 'fixed';
  document.body.style.top = `-${scrollPosition}px`;
  document.body.style.width = '100%';
}

function unlockScroll() {
  document.body.style.position = '';
  document.body.style.top = '';
  document.body.style.width = '';
  window.scrollTo(0, scrollPosition);
}

Chame lockScroll() ao abrir o diálogo e unlockScroll() ao fechá-lo.

Esta técnica armazena a posição de rolagem, fixa o body para que não possa rolar, e então restaura tudo ao fechar. Ela lida com rolagem por toque no iOS porque o body literalmente não pode se mover.

Compensação: Você pode ver um deslocamento de layout se a barra de rolagem desaparecer. Compense adicionando padding-right igual à largura da barra de rolagem ao bloquear.

CSS overscroll-behavior para Encadeamento de Rolagem

A propriedade overscroll-behavior previne o encadeamento de rolagem—quando rolar dentro de um elemento faz com que um elemento pai role após atingir o limite.

dialog {
  overscroll-behavior: contain;
}

dialog::backdrop {
  overscroll-behavior: contain;
}

Versões recentes do Chromium (final de 2025+) melhoraram esse comportamento, fazendo-o funcionar mesmo em contêineres não roláveis. Isso ajuda com o bloqueio de rolagem de diálogo HTML no Chrome, mas outros navegadores ainda não alcançaram isso completamente.

Importante: Soluções modais com CSS overscroll-behavior previnem o encadeamento de rolagem, não a rolagem em si. Se o usuário rolar diretamente no backdrop ou body, overscroll-behavior não vai impedir. Use isso junto com outras técnicas, não como substituto.

Peculiaridades do iOS Safari Que Você Deve Conhecer

O iOS Safari apresenta desafios únicos para prevenir rolagem de fundo em implementações modais:

  • overflow: hidden no body não bloqueia de forma confiável a rolagem por toque
  • O efeito de overscroll elástico pode acionar movimento de fundo
  • O aparecimento do teclado virtual pode causar comportamento de rolagem inesperado

A técnica de body fixo permanece a solução mais confiável. Alguns desenvolvedores também adicionam touch-action: none ao elemento backdrop (não ao diálogo em si), embora isso possa interferir com a rolagem dentro do diálogo se aplicado de forma muito ampla.

Mantenha Seu Diálogo Rolável

Um detalhe crítico: se o conteúdo do seu diálogo exceder a altura da viewport, o próprio diálogo deve rolar. Defina max-height e overflow-y: auto apropriados no diálogo:

dialog {
  max-height: 90vh;
  overflow-y: auto;
}

Bloquear toda a rolagem cria problemas de acessibilidade. Os usuários precisam acessar todo o conteúdo do diálogo.

Uma Nota sobre a Popover API

A Popover API existe para overlays leves, mas não é um substituto direto para diálogos modais. Popovers não bloqueiam a interação de fundo da mesma forma, e o comportamento de bloqueio de rolagem difere. Para experiências modais verdadeiras que requerem bloqueio de rolagem, fique com <dialog> e showModal().

Escolhendo Sua Abordagem

Para aplicações apenas desktop, a abordagem CSS :has() com overflow: hidden geralmente é suficiente. Para confiabilidade cross-browser—especialmente iOS Safari—use a técnica de body fixo com restauração da posição de rolagem. Adicione overscroll-behavior: contain por cima para prevenir encadeamento de rolagem em navegadores compatíveis.

Conclusão

Nenhuma solução funciona perfeitamente em todos os lugares. A técnica de body fixo oferece o bloqueio de rolagem cross-browser mais confiável, particularmente para iOS Safari. Combine-a com overscroll-behavior: contain para proteção adicional contra encadeamento de rolagem em navegadores Chromium. Teste em dispositivos reais, particularmente iOS Safari, e aceite que alguns casos extremos podem exigir compromisso.

Perguntas Frequentes

O iOS Safari lida com rolagem por toque de forma diferente dos navegadores desktop. O sistema de eventos de toque do navegador pode ignorar overflow hidden no elemento body, permitindo rolagem de fundo mesmo quando a propriedade está definida. A técnica de body fixo funciona porque posiciona fisicamente o body fora da tela, tornando a rolagem impossível em vez de apenas oculta.

Sim, pode causar. Quando a barra de rolagem desaparece, o conteúdo pode se deslocar para preencher esse espaço. Para prevenir isso, calcule a largura da barra de rolagem e adicione padding-right equivalente ao body ao bloquear a rolagem. Remova o padding ao desbloquear. Isso mantém o layout consistente durante toda a interação modal.

A Popover API serve a um propósito diferente. Ela cria overlays leves sem bloquear a interação de fundo ou fornecer as mesmas capacidades de bloqueio de rolagem. Para experiências modais verdadeiras onde os usuários devem interagir com o diálogo antes de continuar, use o elemento dialog com showModal para captura de foco adequada e comportamento inerte.

Defina overscroll-behavior contain no diálogo para prevenir encadeamento de rolagem para o fundo. Certifique-se de que seu diálogo tenha max-height e overflow-y auto para que o conteúdo interno role adequadamente. Evite aplicar touch-action none ao diálogo inteiro, pois isso bloqueia a rolagem legítima dentro da área de conteúdo modal.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay