Seite nicht scrollen, wenn ein Modal geöffnet ist

Avatar of Brad Wu
Brad Wu am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Bitte unterbrecht mich, falls ihr das schon mal gehört habt. Man öffnet ein Modal, scrollt darin herum, schließt es und landet an einer anderen Stelle der Seite als dort, wo man das Modal geöffnet hat.

Das liegt daran, dass Modals wie jedes andere Element auf einer Seite sind. Es mag an Ort und Stelle bleiben (vorausgesetzt, das ist seine Funktion), aber der Rest der Seite verhält sich normal.

Siehe den Pen
Vermeide Scrollen des Körpers in Safari, wenn ein Modal-Dialog angezeigt wird
von Geoff Graham (@geoffgraham)
auf CodePen.

Manchmal ist das kein Problem, z. B. bei Bildschirmen, die genau die Höhe des Viewports haben. Bei allem anderen sehen wir uns "Scroll City" gegenüber. Die gute Nachricht ist, dass wir das mit einer Prise CSS (und JavaScript) Trickerei verhindern können.

Beginnen wir mit etwas Einfachem

Wir können einen großen Schritt zur Vermeidung des Seiten-Scrollens beim Öffnen eines Modals machen, indem wir die Höhe des gesamten Bodys auf die volle Höhe des Viewports setzen und den vertikalen Überlauf ausblenden, wenn das Modal geöffnet ist.

body.modal-open {
  height: 100vh;
  overflow-y: hidden;
}

Das ist gut und schön, aber wenn wir durch das <body>-Element gescrollt haben, bevor wir das Modal öffnen, bekommen wir ein kleines horizontales Reflow. Die Breite des Viewports wird um etwa 15 Pixel erweitert, was genau die Breite der Scrollleiste ist.

Siehe den Pen
Vermeide Scrollen des Körpers in Safari, wenn ein Modal-Dialog angezeigt wird
von Geoff Graham (@geoffgraham)
auf CodePen.

Wir passen den rechten Abstand des Bodys etwas an, um das zu vermeiden.

body {
  height: 100vh;
  overflow-y: hidden;
  padding-right: 15px; /* Avoid width reflow */
}

Beachten Sie, dass das Modal kürzer sein muss als die Höhe des Viewports, damit dies funktioniert. Andernfalls ist die Scrollleiste des Bodys notwendig.

Prima, und was ist mit Mobilgeräten?

Diese Lösung funktioniert sowohl auf Desktops als auch auf Android-Mobilgeräten recht gut. Safari für iOS benötigt jedoch etwas mehr Aufmerksamkeit, da der Body immer noch scrollt, wenn ein Modal geöffnet ist und man auf dem Touchscreen tippt und sich bewegt.

Wir können den Body als Workaround auf eine feste Position setzen.

body {
  position: fixed;
}

Funktioniert jetzt! Der Body reagiert nicht mehr, wenn der Bildschirm berührt wird. Es gibt jedoch immer noch ein "kleines" Problem. Nehmen wir an, der Modalauslöser befindet sich weiter unten auf der Seite und wir klicken darauf, um ihn zu öffnen. Prima! Aber jetzt werden wir automatisch wieder zum Anfang des Bildschirms gescrollt, was genauso desorientierend ist wie das Scrollverhalten, das wir zu lösen versuchen.

Siehe den Pen
Vermeide Scrollen des Körpers in Safari, wenn ein Modal-Dialog angezeigt wird
von Geoff Graham (@geoffgraham)
auf CodePen.

Bäh!

Deshalb müssen wir zu JavaScript greifen

Wir können JavaScript verwenden, um die Weiterleitung von Touch-Events zu vermeiden. Wir alle wissen, dass es eine Hintergrundebene geben sollte, wenn ein Modal geöffnet ist. Leider ist stopPropagation bei Touch-Events unter iOS etwas umständlich. Aber preventDefault funktioniert gut. Das bedeutet, dass wir Event-Listener auf jedem DOM-Knoten hinzufügen müssen, der im Modal enthalten ist – nicht nur auf der Hintergrundebene oder der Modal-Box-Ebene. Die gute Nachricht ist, dass viele JavaScript-Bibliotheken dies tun können, einschließlich des guten alten jQuery.

Ach ja, und noch etwas: Was, wenn wir *innerhalb* des Modals scrollen müssen? Wir müssen immer noch eine Reaktion auf ein Touch-Event auslösen, aber wenn wir das obere oder untere Ende des Modals erreichen, müssen wir immer noch die Weiterleitung verhindern. Das scheint sehr komplex, wir sind hier also noch nicht ganz aus dem Schneider.

Verbessern wir den Ansatz mit festem Body

Dies ist das, womit wir gearbeitet haben

body {
  position: fixed;
}

Wenn wir die obere Scrollposition kennen und sie zu unseren CSS hinzufügen, scrollt der Body nicht zurück zum Anfang des Bildschirms, also ist das Problem gelöst. Wir können dies mit JavaScript tun, indem wir den Scroll-Top berechnen und diesen Wert zu den Body-Stilen hinzufügen.

// When the modal is shown, we want a fixed body
document.body.style.position = 'fixed';
document.body.style.top = `-${window.scrollY}px`;

// When the modal is hidden, we want to remain at the top of the scroll position
document.body.style.position = '';
document.body.style.top = '';

Das funktioniert, aber es gibt immer noch ein kleines Problem nach dem Schließen des Modals. Genauer gesagt, es scheint, dass die Seite ihre Scrollposition bereits verliert, wenn das Modal geöffnet ist und der Body auf fest gesetzt ist. Wir müssen also den Speicherort abrufen. Ändern wir unser JavaScript entsprechend.

// When the modal is hidden...
const scrollY = document.body.style.top;
document.body.style.position = '';
document.body.style.top = '';
window.scrollTo(0, parseInt(scrollY || '0') * -1);

Das war's! Der Body scrollt nicht mehr, wenn ein Modal geöffnet ist, und die Scrollposition wird sowohl bei geöffnetem als auch bei geschlossenem Modal beibehalten. Hurra!

Siehe den Pen
Vermeide Scrollen des Körpers in Safari, wenn ein Modal-Dialog angezeigt wird
von Geoff Graham (@geoffgraham)
auf CodePen.