Der Trick mit Viewport-Einheiten auf Mobilgeräten

Avatar of Louis Hoebregts
Louis Hoebregts am

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

Viewport-Einheiten waren schon immer umstritten, und das liegt teilweise daran, wie mobile Browser die Dinge komplizierter gemacht haben, indem sie ihre eigenen Vorstellungen über die Implementierung hatten.

Fallbeispiel: Soll die Scrollleiste bei der vw-Einheit berücksichtigt werden? Was ist mit der Navigation oder den Seitensteuerelementen einer Website – sollten diese in die Berechnung einbezogen werden? Dann gibt es noch physische Eigenschaften der Geräte selbst (hallo, Notch!), die nicht übersehen werden können.

Zuerst ein wenig Kontext

Die Spezifikation ist ziemlich vage, wie Viewport-Einheiten berechnet werden sollen. Bei mobilen Geräten konzentrieren wir uns oft auf die vertikale Höhe, also schauen wir uns speziell die Viewporthöhe (vh) an.

vh-Einheit
Entspricht 1 % der Höhe des initialen Containment-Blocks.

Also ja, keine klare Anleitung, wenn es darum geht, geräte- und browserspezifische Unterschiede zu handhaben.

vh wurde ursprünglich vom aktuellen Viewport Ihres Browsers berechnet. Wenn Sie Ihren Browser öffneten und eine Website zu laden begannen, war 1vh gleich 1 % der Bildschirmhöhe abzüglich der Browser-Oberfläche.

Aber! Wenn Sie mit dem Scrollen beginnen, ist es eine andere Geschichte. Sobald Sie an einem Teil der Browser-Oberfläche, wie der Adressleiste, vorbeikommen, würde sich der vh-Wert aktualisieren und das Ergebnis war ein unschöner Sprung im Inhalt.

Safari für iOS war einer der ersten mobilen Browser, der seine Implementierung aktualisierte, indem er einen festen Wert für vh basierend auf der maximalen Bildschirmhöhe definierte. Dadurch würde der Benutzer keine Sprünge auf der Seite erleben, sobald die Adressleiste außer Sicht geriet. Chromes mobiler Browser zog etwa ein Jahr später nach.

Zum Zeitpunkt der Erstellung dieses Artikels gibt es ein Ticket, das dies in Firefox Android beheben soll.

Während die Verwendung eines festen Wertes gut ist, bedeutet dies auch, dass Sie kein volles Element haben können, wenn die Adressleiste sichtbar ist. Der untere Teil Ihres Elements wird abgeschnitten.

Ein Element wird unten abgeschnitten, wenn die Adressleiste sichtbar ist (links), aber wir wollen das Ganze (rechts).

CSS Custom Properties: Der Trick zur korrekten Größenbestimmung

Mir kam die Idee, dass CSS Custom Properties und ein paar Zeilen JavaScript der perfekte Weg sein könnten, um die konsistente und korrekte Größenbestimmung zu erreichen, die ich benötigte.

In JavaScript kann man immer den Wert des aktuellen Viewports über die globale Variable window.innerHeight abrufen. Dieser Wert berücksichtigt die Schnittstelle des Browsers und wird aktualisiert, wenn sich seine Sichtbarkeit ändert. Der Trick besteht darin, den Viewport-Wert in einer CSS-Variable zu speichern und diese auf das Element anzuwenden, *anstatt* der vh-Einheit.

Nehmen wir an, unsere CSS Custom Variable ist --vh für dieses Beispiel. Das bedeutet, dass wir sie in unserem CSS wie folgt anwenden wollen:

.my-element {
  height: 100vh; /* Fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
}

Okay, das ist eingerichtet. Holen wir uns jetzt die innere Höhe des Viewports in JavaScript.

// First we get the viewport height and we multiple it by 1% to get a value for a vh unit
let vh = window.innerHeight * 0.01;
// Then we set the value in the --vh custom property to the root of the document
document.documentElement.style.setProperty('--vh', `${vh}px`);

Wir haben JavaScript angewiesen, die Höhe des Viewports zu ermitteln und sie dann durch 1/100 davon zu teilen, um einen Wert zu erhalten, der unserer Viewporthöhen-Einheit zugewiesen werden kann. Dann baten wir JS höflich, die CSS-Variable (--vh) am :root zu erstellen.

Als Ergebnis können wir nun --vh als Höhenwert verwenden, so wie wir es mit jeder anderen vh-Einheit tun würden, sie mit 100 multiplizieren und wir haben die volle gewünschte Höhe.

Es gibt eine weitere Korrektur dafür, die vor kurzem aufgetaucht ist. Matt Smith dokumentiert sie hier. Der Trick ist min-height: -webkit-fill-available; auf dem Body als progressive Verbesserung über 100vh, was auf iOS-Geräten funktionieren sollte.

Moment mal! Noch ein kleines Detail.

Obwohl unsere Arbeit an dieser Stelle erledigt zu sein scheint, haben diejenigen unter Ihnen mit einem geschulten Auge für Details vielleicht bemerkt, dass JavaScript ausgeführt wird, aber die Größe unseres Elements nicht aktualisiert, wenn sich die Höhe des Viewports ändert. Probieren Sie es ruhig aus und ändern Sie die Größe der obigen Demo.

Wir können den Wert von --vh aktualisieren, indem wir auf das resize-Ereignis des Fensters hören. Dies ist nützlich, falls der Benutzer die Ausrichtung des Geräts ändert, z. B. von Querformat zu Hochformat, oder die Navigation beim Scrollen aus dem Sichtfeld gerät.

// We listen to the resize event
window.addEventListener('resize', () => {
  // We execute the same script as before
  let vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
});

⚠️ Das Aktualisieren des Wertes von --vh löst eine Neuberechnung der Seite aus und der Benutzer kann dadurch einen Sprung erleben. Aus diesem Grund *rate ich nicht dazu, diesen Trick für jedes Projekt zu verwenden oder die gesamte Verwendung der vh-Einheit zu ersetzen*, sondern nur, wenn Sie von Ihren Benutzern einen exakten Viewport-Einheitswert benötigen.

Außerdem möchten Sie vielleicht eine Debounce-Methode für das Resize-Ereignis implementieren, um zu vermeiden, dass zu viele Ereignisse ausgelöst werden, während der Benutzer sein Browserfenster vergrößert. Mehr dazu erfahren Sie in diesem Artikel: Debouncing und Throttling erklärt mit Beispielen

Sie können die obige Demo nun in der Größe ändern und feststellen, dass die CSS-Variable entsprechend aktualisiert wird.

Obwohl ich diese Technik kürzlich in einem Projekt verwendet habe und sie wirklich geholfen hat, sollten Sie immer zweimal nachdenken, bevor Sie das Standardverhalten des Browsers ersetzen. (Zum Beispiel taucht dies häufig bei ::focus auf.) Außerdem tendieren Browser heutzutage dazu, sich sehr schnell zu aktualisieren, also seien Sie sich bewusst, dass eine heutige Lösung morgen vielleicht nicht mehr funktioniert.

In der Zwischenzeit hoffe ich, dass dieser Artikel Ihnen hilft! 👋

Hier ist ein Vorschlag für vhc- und vwc-Einheiten, die in all dem eine Rettung sein könnten.