Supposons qu'il y ait plusieurs fenêtres d'affichage.
BRRRRAAAAAAAMMMMMMMMMM
La fenêtre d'affichage que vous utilisez actuellement est en réalité une fenêtre dans une fenêtre d'affichage.
BRRRRAAAAAAAMMMMMMMMMM
Parfois, les données fournies par le DOM font référence à l'une de ces fenêtres d'affichage et non à l'autre.
BRRRRAAAAM... attendez quoi ?
C'est vrai. Regardez:
Fenêtre d'affichage de la mise en page ou fenêtre d'affichage visuelle
La vidéo ci-dessus montre une page Web que l'on fait défiler et que l'on pince pour zoomer, ainsi qu'à droite une mini-carte indiquant la position des fenêtres d'affichage sur la page.
Tout est assez simple lors du défilement régulier. La zone verte représente la fenêtre d'affichage de la mise en page, à laquelle les éléments position: fixed
restent.
Les choses deviennent bizarres lorsque le zoom par pincement est introduit. Le cadre rouge représente la fenêtre d'affichage, qui correspond à la partie de la page que nous pouvons réellement voir. Cette fenêtre d'affichage peut se déplacer tandis que les éléments position: fixed
restent là où ils se trouvaient, associés à la fenêtre d'affichage de mise en page. Si nous effectuons un panoramique à une limite de la fenêtre d'affichage de mise en page, la fenêtre d'affichage de mise en page est déplacée avec elle.
Améliorer la compatibilité
Malheureusement, les API Web ne sont pas les mêmes en termes de fenêtre d'affichage, mais aussi d'un navigateur à l'autre.
Par exemple, element.getBoundingClientRect().y
renvoie le décalage dans la fenêtre d'affichage de la mise en page. C'est cool, mais nous voulons souvent la position dans la page. C'est pourquoi nous écrivons:
element.getBoundingClientRect().y + window.scrollY
Cependant, de nombreux navigateurs utilisent la fenêtre d'affichage pour window.scrollY
, ce qui signifie que le code ci-dessus ne fonctionne pas lorsque l'utilisateur pince pour zoomer.
Chrome 61 modifie window.scrollY
pour qu'il fasse référence à la fenêtre d'affichage de mise en page à la place, ce qui signifie que le code ci-dessus fonctionne même lorsque l'utilisateur effectue un zoom par pincement. En fait, les navigateurs modifient lentement toutes les propriétés de position pour faire référence à la fenêtre d'affichage de mise en page.
À l'exception d'une nouvelle propriété...
Présenter la fenêtre d'affichage visuelle au script
Une nouvelle API expose la fenêtre d'affichage visuelle en tant que window.visualViewport
. Il s'agit d'un brouillon de spécification, avec approbation internavigateur, qui arrive dans Chrome 61.
console.log(window.visualViewport.width);
Voici ce que window.visualViewport
nous donne:
visualViewport établissements |
|
---|---|
offsetLeft
|
Distance entre le bord gauche de la fenêtre d'affichage visuelle et la fenêtre d'affichage de la mise en page, en pixels CSS. |
offsetTop
|
Distance entre le bord supérieur de la fenêtre d'affichage et la fenêtre d'affichage de la mise en page, en pixels CSS. |
pageLeft
|
Distance entre le bord gauche de la fenêtre d'affichage visuelle et la limite de gauche du document, en pixels CSS. |
pageTop
|
Distance entre le bord supérieur de la fenêtre d'affichage et la limite supérieure du document, en pixels CSS. |
width
|
Largeur de la fenêtre d'affichage visuelle en pixels CSS. |
height
|
Hauteur de la fenêtre d'affichage en pixels CSS. |
scale
|
Échelle appliquée en effectuant un zoom par pincement. Si le contenu fait deux fois la taille du contenu en raison du zoom, la valeur renvoyée est 2 . Cela n'est pas affecté par devicePixelRatio .
|
Il existe également quelques événements:
window.visualViewport.addEventListener('resize', listener);
visualViewport événements |
|
---|---|
resize
|
Déclenché lorsque width , height ou scale change.
|
scroll
|
Déclenché lorsque offsetLeft ou offsetTop change.
|
Démonstration
La vidéo au début de cet article a été créée avec visualViewport
. Consultez-la dans Chrome 61 ou une version ultérieure. Elle utilise visualViewport
pour que la mini-carte reste en haut à droite de la fenêtre d'affichage et applique une échelle inverse afin qu'elle s'affiche toujours à la même taille, malgré le pincement de zoom.
Problèmes
Les événements ne se déclenchent que lorsque la fenêtre d'affichage visuelle change
Cela semble évident, mais cela m'a surpris(e) quand j'ai joué avec visualViewport
pour la première fois.
Si la fenêtre d'affichage de mise en page est redimensionnée, mais pas la fenêtre d'affichage visuelle, vous ne recevez pas d'événement resize
. Cependant, il est inhabituel que la fenêtre d'affichage de mise en page soit redimensionnée sans que la fenêtre d'affichage visuelle ne change également de largeur/hauteur.
Le vrai piège, c'est le défilement. En cas de défilement, mais que la fenêtre d'affichage visuelle reste statique par rapport à la fenêtre d'affichage de mise en page, vous ne recevez pas d'événement scroll
sur visualViewport
, ce qui est très courant. Lors du défilement normal des documents, la fenêtre d'affichage visuelle reste verrouillée en haut à gauche de la fenêtre d'affichage de la mise en page. Par conséquent, scroll
ne se déclenche pas sur visualViewport
.
Si vous souhaitez être informé de toutes les modifications apportées à la fenêtre d'affichage visuelle, y compris pageTop
et pageLeft
, vous devez également écouter l'événement de défilement de la fenêtre:
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
Évitez de dupliquer des tâches avec plusieurs écouteurs
Comme pour l'écoute de scroll
et resize
sur la fenêtre, vous serez probablement amené à appeler une sorte de fonction "update". Cependant, il est courant qu'un grand nombre de ces événements se produisent en même temps. Si l'utilisateur redimensionne la fenêtre, resize
se déclenchera, mais aussi souvent scroll
. Pour améliorer les performances, évitez de gérer la modification plusieurs fois:
// 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
});
}
J'ai signalé un problème spécifique, car je pense qu'il existe une meilleure solution, par exemple un seul événement update
.
Les gestionnaires d'événements ne fonctionnent pas
En raison d'un bug dans Chrome, cette opération ne fonctionne pas:
Buggy (utilise un gestionnaire d'événements)
visualViewport.onscroll = () => console.log('scroll!');
Au lieu de cela :
Fonctionne (utilise un écouteur d'événements)
visualViewport.addEventListener('scroll', () => console.log('scroll'));
Les valeurs de décalage sont arrondies
Je crois qu'il s'agit d'un autre bug de Chrome.
offsetLeft
et offsetTop
sont arrondis, ce qui est assez inexact une fois que l'utilisateur a fait un zoom avant. Vous pouvez voir les problèmes que cela pose problème lors de la démonstration. Si l'utilisateur fait un zoom avant et fait un panoramique lentement, la mini-carte s'ancre entre les pixels non zoomés.
Le taux d'événements est lent
Comme les autres événements resize
et scroll
, ils ne se déclenchent pas tous les frames, en particulier sur mobile. Vous pouvez le constater lors de la démonstration. Lorsque vous pincez le zoom, la mini-carte ne parvient pas à rester verrouillée dans la fenêtre d'affichage.
Accessibilité
Dans la démonstration, j'ai utilisé visualViewport
pour neutraliser le zoom par pincement de l'utilisateur. Cette démonstration est tout à fait logique, mais vous devez bien réfléchir avant d'effectuer une action qui ignore le désir de l'utilisateur de zoomer.
visualViewport
peut être utilisé pour améliorer l'accessibilité. Par exemple, si l'utilisateur fait un zoom avant, vous pouvez choisir de masquer les éléments décoratifs position: fixed
pour les empêcher de le faire. Mais là encore, veillez à ne pas cacher quelque chose
que l'utilisateur essaie d'examiner de plus près.
Vous pouvez envisager de publier sur un service d'analyse lorsque l'utilisateur fait un zoom avant. Cela peut vous aider à identifier les pages avec lesquelles les utilisateurs rencontrent des difficultés au niveau de zoom par défaut.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
Voilà, c'est terminé ! visualViewport
est une petite API qui résout les problèmes de compatibilité en cours de route.