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:
Fehlerhaft – verwendet einen Event-Handler
visualViewport.onscroll = () => console.log('scroll!');
Gehen Sie in diesem Fall so vor:
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.