Jetzt neu: „visualViewport“

Archibald
Jake Archibald

Was wäre, wenn ich Ihnen sagen würde, dass es mehr als einen Darstellungsbereich gibt?

BRRRRAAAAAAAMMMMMMMMMM

Und der Darstellungsbereich, den Sie gerade verwenden, ist eigentlich ein Darstellungsbereich innerhalb eines Darstellungsbereichs.

BRRRRAAAAAAAMMMMMMMMMM

Manchmal beziehen sich die Daten, die Sie vom DOM erhalten, auf einen dieser Darstellungsbereich und nicht auf den anderen.

BRRRAAAAM... Moment, was?

Hier ist die richtige Antwort:

Layout und Darstellungsbereich im Vergleich

Im Video oben ist eine Webseite zu sehen, auf der gescrollt wird, und durch Auseinander- und Zusammenziehen der Finger wird gezoomt. Auf der rechten Seite ist eine Minikarte mit der Position der Darstellungsbereiche auf der Seite zu sehen.

Beim regulären Scrollen sind die Dinge ziemlich einfach. Der grüne Bereich stellt den Layoutdarstellungsbereich dar, an den die position: fixed-Elemente beibehalten werden.

Wenn Sie die Funktion zum Zoomen auseinander- und zusammenziehen, Das rote Feld stellt den visuellen Darstellungsbereich dar, also den Teil der Seite, den wir tatsächlich sehen können. Dieser Darstellungsbereich kann sich verschieben, während position: fixed-Elemente an der Stelle verbleiben, an der sie an den Layout-Darstellungsbereich angehängt waren. Wird an die Grenze des Darstellungsbereichs des Layout geschwenkt, wird der Darstellungsbereich

Verbesserte Kompatibilität

Leider sind Web-APIs weder hinsichtlich des Darstellungsbereichs noch auf den jeweiligen Browser einheitlich.

element.getBoundingClientRect().y gibt beispielsweise den Versatz innerhalb des Layoutdarstellungsbereichs zurück. Das ist toll, aber wir möchten oft die Position auf der Seite, also schreiben wir:

element.getBoundingClientRect().y + window.scrollY

In vielen Browsern wird jedoch der visuelle Darstellungsbereich für window.scrollY verwendet. Das bedeutet, dass der obige Code nicht mehr funktioniert, wenn der Nutzer mit den Fingern zoomt.

In Chrome 61 wird window.scrollY so geändert, dass stattdessen auf den Layout-Darstellungsbereich verwiesen wird. Der obige Code funktioniert also auch, wenn die Finger auseinander- und zusammengezogen werden. Tatsächlich ändern Browser langsam alle Positionseigenschaften, um auf den Layout-Darstellungsbereich zu verweisen.

Mit Ausnahme einer neuen Property...

Visuellen Darstellungsbereich für Skript freigeben

Eine neue API stellt den visuellen Darstellungsbereich als window.visualViewport bereit. Es handelt sich um einen Entwurf mit browserübergreifender Genehmigung, der auf Chrome 61 ausgerichtet ist.

console.log(window.visualViewport.width);

Von window.visualViewport erhalten wir Folgendes:

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 Abstand zwischen dem linken Rand des visuellen Darstellungsbereichs und der linken Begrenzung des Dokuments in CSS-Pixeln.
pageTop Abstand zwischen dem oberen Rand des visuellen Darstellungsbereichs und der oberen Begrenzung des Dokuments in CSS-Pixeln.
width Breite des visuellen Darstellungsbereichs in CSS-Pixeln.
height Höhe des visuellen Darstellungsbereichs in CSS-Pixeln
scale Die durch Auseinander- und Zusammenziehen der Finger angewendete Skalierung. Ist der Inhalt aufgrund des Zooms doppelt so groß, wird 2 zurückgegeben. devicePixelRatio hat keinen Einfluss darauf.

Außerdem gibt es einige Veranstaltungen:

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 ändern.

Demo

Das Video zu Beginn dieses Artikels wurde mit visualViewport erstellt. Sehen Sie sich dies in Chrome 61+ an. Dabei wird visualViewport verwendet, damit die Minikarte oben rechts im Darstellungsbereich fixiert ist. Außerdem wird eine umgekehrte Skalierung angewendet, sodass die Größe trotz Auseinander- und Zusammenziehen der Finger immer gleich bleibt.

Erwischt

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

Das scheint mir selbstverständlich zu sein, aber als ich zum ersten Mal mit visualViewport gespielt habe, hat es mich gefangen.

Wenn die Größe des Layout-Darstellungsbereichs angepasst wird, beim visuellen Darstellungsbereich jedoch nicht, wird kein resize-Ereignis zurückgegeben. Es ist jedoch ungewöhnlich, dass die Größe des Layout-Darstellungsbereichs geändert wird, ohne dass sich auch die Breite/Höhe des visuellen Darstellungsbereichs ändert.

Das eigentliche Problem ist das Scrollen. Wenn gescrollt wird, der visuelle Darstellungsbereich im Vergleich zum Darstellungsbereich des Layouts aber statisch bleibt, wird bei visualViewport kein scroll-Ereignis zurückgegeben. Das kommt sehr häufig vor. Beim regulären Scrollen bleibt der visuelle Darstellungsbereich links oben im Darstellungsbereich des Layouts gesperrt, sodass scroll bei visualViewport nicht ausgelöst wird.

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

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

Doppelte Arbeiten mit mehreren Listenern vermeiden

Ähnlich wie beim Überwachen von scroll und resize für das Fenster wird wahrscheinlich eine Art "Update"-Funktion aufgerufen. Es ist jedoch üblich, dass viele dieser Ereignisse gleichzeitig stattfinden. Wenn der Nutzer die Größe des Fensters ändert, wird resize ausgelöst, häufig aber auch scroll. Um die Leistung zu verbessern, sollten Sie die Änderung nicht mehrmals verarbeiten:

// 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 den Spezifikationen dafür gemeldet, da es möglicherweise eine bessere Lösung gibt, z. B. ein einzelnes update-Ereignis.

Event-Handler funktionieren nicht

Aufgrund eines Chrome-Programmfehlers funktioniert Folgendes nicht:

Don'ts

Fehlerhaft – verwendet einen Event-Handler

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

Gehen Sie in diesem Fall so vor:

Das sollten Sie tun:

Funktioniert – verwendet einen Event-Listener

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

Offsetwerte sind gerundet

Ich denke, das ist ein weiterer Chrome-Fehler.

offsetLeft und offsetTop sind gerundet, was ziemlich ungenau ist, wenn der Nutzer herangezoomt hat. Sie können die Probleme in der Demo sehen. Wenn der Nutzer langsam heranzoomt und schwenkt, wechselt die Minikarte zwischen nicht gezoomten Pixeln.

Langsame Ereignisrate

Wie bei anderen resize- und scroll-Ereignissen wird auch bei diesen nicht jeder Frame ausgelöst, insbesondere auf Mobilgeräten. Sie können dies in der Demo sehen. Wenn Sie die Minikarte mit den Fingern heranzoomen, bleibt sie nicht mehr am Darstellungsbereich fixiert.

Barrierefreiheit

In der Demo habe ich visualViewport verwendet, um dem Zoomen durch Auseinander- und Zusammenziehen der Finger entgegenzuwirken. Für diese Demo ist es sinnvoll, aber Sie sollten sorgfältig überlegen, bevor Sie etwas tun, das den Wunsch des Nutzers, heranzuzoomen, außer Kraft setzt.

visualViewport kann zur Verbesserung der Barrierefreiheit verwendet werden. Wenn der Nutzer beispielsweise heranzoomt, können Sie dekorative position: fixed-Artikel ausblenden, damit sie nicht im Weg sind. Aber achten Sie darauf, dass Sie nichts verbergen, das sich die Nutzenden genauer ansehen möchten.

Sie könnten Inhalte in einem Analysedienst posten, wenn der Nutzer heranzoomt. So können Sie Seiten ermitteln, bei denen Nutzer auf der Standard-Zoomstufe Schwierigkeiten haben.

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

Webseite. visualViewport ist eine nette kleine API, die Kompatibilitätsprobleme auf dem Weg behebt.