Empêcher le défilement de la page sous une boîte de dialogue modale

Fonctionnalités Web : overscroll-behavior <dialog> ::backdrop :modal

Lorsqu'un élément <dialog> est ouvert en tant que boîte de dialogue modale à l'aide de showModal(), le navigateur le place dans la couche supérieure et marque le reste du document comme inerte. Bien que cela bloque les clics du pointeur et le focus clavier sur la page sous-jacente, cela n'empêche pas le défilement :

  1. Si vous faites défiler la page alors que le pointeur ou le geste tactile se trouve sur l'élément ::backdrop, la page défile en dessous.
  2. Le défilement dans le <dialog> est lié à la page sous-jacente une fois que la boîte de dialogue atteint sa limite de défilement, ou immédiatement si le contenu de la boîte de dialogue ne dépasse pas.

Conformément à la spécification CSS Overscroll Behavior, overscroll-behavior s'applique à tous les éléments de conteneur de défilement, y compris les conteneurs de défilement non défilables (éléments avec overflow: hidden ou overflow: auto dont le contenu ne déborde pas actuellement). En combinant overscroll-behavior: contain sur <dialog> avec overflow: hidden et overscroll-behavior: contain sur dialog::backdrop, vous pouvez empêcher le défilement de la page sous-jacente en utilisant uniquement du CSS.

Implémentation

1. Contenir le chaînage de défilement sur <dialog>

La feuille de style user-agent définit overflow: auto sur <dialog>, ce qui en fait un conteneur de défilement par défaut. Appliquez overscroll-behavior: contain (ou none) à dialog:modal (ou dialog).

2. Transformer ::backdrop en conteneur de défilement et contenir son défilement

Par défaut, dialog::backdrop est défini sur overflow: visible, ce qui signifie qu'il ne s'agit pas d'un conteneur de défilement et qu'il ignore overscroll-behavior. Vous devez définir overflow: hidden sur dialog::backdrop pour en faire un conteneur de défilement non déroulant, et l'associer à overscroll-behavior: contain afin que les gestes de défilement effectués sur l'arrière-plan soient interceptés et n'atteignent pas le document sous-jacent.

Exemple de code

dialog:modal {
  /* MANDATORY: Prevent scroll gestures inside the modal (even when its content
     does not overflow) from chaining to the underlying page. */
  overscroll-behavior: contain;
}

dialog::backdrop {
  /* MANDATORY: ::backdrop defaults to overflow: visible (not a scroll container).
     Set overflow: hidden so it becomes a non-scrollable scroll container that
     honors overscroll-behavior. */
  overflow: hidden;
  overscroll-behavior: contain;
}

Bonnes pratiques et contraintes

  • OBLIGATOIRE : définissez overscroll-behavior: contain sur l'élément <dialog> lui-même (à l'aide du sélecteur dialog:modal) et sur dialog::backdrop. Sans cette déclaration, les gestes de défilement sur l'arrière-plan ou dans la boîte de dialogue seront toujours associés à la page.
  • OBLIGATOIRE : Définissez overflow: hidden (ou overflow: auto) sur dialog::backdrop pour qu'il devienne un conteneur de défilement. Sans cette déclaration, ::backdrop est défini par défaut sur overflow: visible et overscroll-behavior n'aura aucun effet.
  • N'UTILISEZ PAS overflow: clip ou overflow: visible sur dialog::backdrop ou dialog. Aucune de ces valeurs n'établit de conteneur de défilement. overscroll-behavior ne s'appliquera donc pas.
  • N'appliquez PAS overflow: hidden ou position: fixed à <html> ou <body> pour verrouiller le défilement en arrière-plan lorsqu'une boîte de dialogue s'ouvre. La modification du dépassement de la racine supprime la barre de défilement de la page (ce qui entraîne un décalage de la mise en page horizontale), peut réinitialiser ou faire sauter la position de défilement de la page, et peut endommager les éléments position: sticky de la page sous-jacente.

Stratégies de remplacement

Bien que la propriété CSS overscroll-behavior soit compatible avec tous les principaux navigateurs pour les conteneurs de défilement dont le contenu dépasse, le respect de overscroll-behavior sur les conteneurs de défilement non défilables est une spécification plus récente qui ne peut pas être détectée par les fonctionnalités.

N'essayez PAS de détecter la compatibilité avec les conteneurs non déroulants à l'aide de @supports (overscroll-behavior: contain) ou CSS.supports('overscroll-behavior', 'contain'). Tous les navigateurs récents sont compatibles avec la syntaxe de la propriété overscroll-behavior: contain et renvoient true, même s'ils ne l'appliquent que lorsque le contenu déborde.

Traitez plutôt le confinement du défilement sur les conteneurs de défilement non défilables comme une amélioration progressive :

  • Dans les navigateurs compatibles avec overscroll-behavior sur les conteneurs de défilement non défilables, le défilement de la page en arrière-plan est complètement empêché à la fois sur dialog::backdrop et à l'intérieur de <dialog> (que son contenu déborde ou non).
  • Dans les navigateurs qui ne sont pas encore compatibles avec overscroll-behavior sur les conteneurs de défilement non défilables, overscroll-behavior: contain sur dialog empêche toujours le chaînage du défilement lorsque le contenu de la boîte de dialogue dépasse et est défilable. Pendant ce temps, showModal() piège toujours le focus clavier et marque le document d'arrière-plan inert.
  • Il est recommandé de s'appuyer sur l'amélioration progressive plutôt que sur les anciennes solutions de contournement qui appliquent overflow: hidden ou position: fixed à <html> ou <body>, car cela ne supprime pas la barre de défilement racine, ne provoque pas de décalages de mise en page horizontaux, ne fait pas sauter la position de défilement de la page et ne casse pas les éléments position: sticky de la page sous-jacente.