Jetzt neu: „visualViewport“

Jake Archibald
Jake Archibald

Was, wenn ich Ihnen sage, dass es mehr als einen Darstellungsbereich gibt?

BRRRRAAAAAAAMMMMMMMMMM

Der Darstellungsbereich, den Sie gerade verwenden, ist in Wirklichkeit ein Darstellungsbereich in einem Darstellungsbereich.

BRRRRAAAAAAAMMMMMMMMMM

Manchmal beziehen sich die Daten im DOM auf einen dieser Ansichten und nicht auf den anderen.

BRRRRAAAAM… Moment mal, was?

Das stimmt. Sehen Sie sich das an:

Layout- und visueller Darstellungsbereich

Im Video oben wird eine Webseite gescrollt und per Pinch-Zoom herangezoomt. Rechts ist eine Minikarte zu sehen, die die Position der Ansichten auf der Seite zeigt.

Beim normalen Scrollen ist alles ganz einfach. Der grüne Bereich stellt den Layout-Darstellungsbereich dar, an den sich position: fixed-Elemente halten.

Es wird noch komplizierter, wenn das Zoomen durch Zusammenziehen und Spreizen der Finger eingeführt wird. Der rote Rahmen stellt den visuellen Darstellungsbereich dar, also den Teil der Seite, den wir tatsächlich sehen. Dieser Darstellungsbereich kann verschoben werden, während position: fixed Elemente an ihrem Platz bleiben, da sie am Layout-Darstellungsbereich angehängt sind. Wenn wir an einer Grenze des Layout-Darstellungsbereichs schwenken, wird der Layout-Darstellungsbereich mitgeschleppt.

Kompatibilität verbessern

Leider sind Web-APIs nicht einheitlich in Bezug auf den Viewport, auf den sie sich beziehen, und auch nicht einheitlich in verschiedenen Browsern.

element.getBoundingClientRect().y gibt beispielsweise den Offset innerhalb des Layout-Darstellungsbereichs zurück. Das ist cool, aber oft möchten wir die Position auf der Seite wissen. Daher schreiben wir:

element.getBoundingClientRect().y + window.scrollY

Viele Browser verwenden jedoch den visuellen Darstellungsbereich für window.scrollY. Das bedeutet, dass der Code oben bricht, wenn der Nutzer heranzoomt.

In Chrome 61 bezieht sich window.scrollY stattdessen auf das Layout-Viewport. Das bedeutet, dass der Code oben auch bei Pinch-Zoom funktioniert. Tatsächlich ändern Browser nach und nach alle Positionierungseigenschaften, sodass sie sich auf den Layout-Darstellungsbereich beziehen.

Mit Ausnahme einer neuen Property…

Visuellen Darstellungsbereich für Script freigeben

Eine neue API stellt den visuellen Darstellungsbereich als window.visualViewport bereit. Es handelt sich um eine Entwurfsspezifikation mit browserübergreifender Genehmigung, die in Chrome 61 eingeführt wird.

console.log(window.visualViewport.width);

window.visualViewport bietet folgende Vorteile:

visualViewport Unterkünfte
offsetLeft Abstand zwischen dem linken Rand des visuellen Darstellungsbereichs und dem Layout-Darstellungsbereich in CSS-Pixeln.
offsetTop Abstand zwischen dem oberen Rand des visuellen Darstellungsbereichs und dem Layout-Darstellungsbereich in CSS-Pixeln.
pageLeft Der Abstand zwischen dem linken Rand des visuellen Darstellungsbereichs und der linken Begrenzung des Dokuments in CSS-Pixeln.
pageTop Der Abstand zwischen dem oberen Rand des visuellen Darstellungsbereichs und dem oberen Rand des Dokuments in CSS-Pixeln.
width Breite des visuellen Darstellungsbereichs in CSS-Pixeln.
height Höhe des visuellen Darstellungsbereichs in CSS-Pixeln.
scale Die Skalierung, die durch Zusammen- und Auseinanderziehen der Finger angewendet wird. Wenn die Inhalte durch Zoomen doppelt so groß sind, wird 2 zurückgegeben. devicePixelRatio hat keine Auswirkungen darauf.

Es gibt auch einige Ereignisse:

window.visualViewport.addEventListener('resize', listener);
visualViewport Ereignisse
resize Wird ausgelöst, wenn sich width, height oder scale ändert.
scroll Wird ausgelöst, wenn sich offsetLeft oder offsetTop ändert.

Demo

Das Video am Anfang dieses Artikels wurde mit visualViewport erstellt. Sehen Sie sich das Video in Chrome 61 und höher an. Mit visualViewport wird die Minikarte rechts oben im visuellen Viewport fixiert und es wird ein umgekehrter Maßstab angewendet, damit sie trotz Zusammen- und Auseinanderziehen der Finger immer gleich groß erscheint.

Gotchas

Ereignisse werden nur ausgelöst, wenn sich der visuelle Darstellungsbereich ändert

Das mag offensichtlich erscheinen, aber ich habe es erst gemerkt, als ich zum ersten Mal mit visualViewport experimentiert habe.

Wenn sich das Layout-Viewport ändert, das visuelle Viewport aber nicht, wird kein resize-Ereignis ausgelöst. Es ist jedoch ungewöhnlich, dass sich die Größe des Layout-Darstellungsbereichs ändert, ohne dass sich auch die Breite/Höhe des visuellen Darstellungsbereichs ändert.

Das Problem liegt beim Scrollen. Wenn der Nutzer scrollt, der visuelle Darstellungsbereich aber im Vergleich zum Layout-Darstellungsbereich statisch bleibt, wird bei visualViewport kein scroll-Ereignis ausgelöst. Das ist sehr häufig der Fall. Beim normalen Scrollen des Dokuments bleibt der visuelle Darstellungsbereich oben links im Layout-Darstellungsbereich fixiert. Daher wird scroll nicht bei visualViewport ausgelöst.

Wenn Sie über alle Änderungen am visuellen Darstellungsbereich informiert werden möchten, einschließlich pageTop und pageLeft, müssen Sie auch das Scrollereignis des Fensters überwachen:

visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);

Mehrere Listener vermeiden

Ähnlich wie beim Zuhören auf scroll und resize im Fenster wird wahrscheinlich eine Art „Update“-Funktion aufgerufen. Es kommt jedoch häufig vor, dass viele dieser Ereignisse gleichzeitig auftreten. Wenn der Nutzer die Größe des Fensters ändert, wird resize ausgelöst, aber häufig auch scroll. Vermeiden Sie es, die Änderung mehrmals zu verarbeiten, um die Leistung zu verbessern:

// Add listeners
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
addEventListener('scroll', update);

let pendingUpdate = false;

function update() {
    // If we're already going to handle an update, return
    if (pendingUpdate) return;

    pendingUpdate = true;

    // Use requestAnimationFrame so the update happens before next render
    requestAnimationFrame(() => {
    pendingUpdate = false;

    // Handle update here
    });
}

Ich habe ein Problem mit der Spezifikation eingereicht, da ich der Meinung bin, dass es eine bessere Möglichkeit gibt, z. B. ein einzelnes update-Ereignis.

Event-Handler funktionieren nicht

Aufgrund eines Chrome-Bugs funktioniert das nicht:

Don'ts

Fehlerhaft – verwendet einen Event-Handler

visualViewport.onscroll = () => console.log('scroll!');

Gehen Sie in diesem Fall so vor:

Do

Funktioniert – verwendet einen Event-Listener

visualViewport.addEventListener('scroll', () => console.log('scroll'));

Offsetwerte werden gerundet.

Ich denke (oder hoffe), dass es sich um einen weiteren Chrome-Fehler handelt.

offsetLeft und offsetTop sind gerundet, was ziemlich ungenau ist, wenn der Nutzer heranzoomt. Die Probleme dabei sehen Sie in der Demo: Wenn der Nutzer heranzoomt und langsam schwenkt, springt die Minikarte zwischen nicht herangezoomten Pixeln.

Die Ereignisrate ist zu niedrig

Wie andere resize- und scroll-Ereignisse werden diese nicht bei jedem Frame ausgelöst, insbesondere nicht auf Mobilgeräten. Das können Sie in der Demo sehen: Wenn Sie heranzoomen, bleibt die Minikarte nicht immer im Darstellungsbereich.

Bedienungshilfen

In der Demo habe ich visualViewport verwendet, um das Zoomen per Zusammenziehen und Spreizen der Finger des Nutzers zu verhindern. Für diese Demo ist es sinnvoll, aber Sie sollten gut überlegen, bevor Sie etwas tun, das den Wunsch des Nutzers, heranzuzoomen, überschreibt.

visualViewport kann die Barrierefreiheit verbessern. Wenn der Nutzer beispielsweise heranzoomt, können Sie dekorative position: fixed-Elemente ausblenden, damit sie nicht im Weg sind. Achten Sie aber darauf, dass Sie nichts verbergen, was der Nutzer genauer sehen möchte.

Sie könnten beispielsweise einen Analytics-Dienst benachrichtigen, wenn der Nutzer heranzoomt. So können Sie Seiten identifizieren, auf denen Nutzer bei der Standardzoomstufe Schwierigkeiten haben.

visualViewport.addEventListener('resize', () => {
    if (visualViewport.scale > 1) {
    // Post data to analytics service
    }
});

Webseite. visualViewport ist eine praktische kleine API, die Kompatibilitätsprobleme behebt.