Quand on fait du développement d'interface, il n'est pas rare de devoir corriger la mise en page selon les dimensions d'affichage de la fenêtre.
Ce n'est pas compliqué à trouver, mais assez laborieux et chronophage. Autant avoir les informations en temps réel.
Une solution consiste à surveiller en temps réel les données concernées, qu'il s'agisse de l'orientation, des dimensions de la zone d'affichage qui ne sont pas celles du navigateur, ou du viewport.
On va profiter de la détection du change sur les viewport et de ResizeObserver.
On utilise window.matchMedia, le tout en mode passif, et zou !
Enfin, pour améliorer la performance, il est important de veiller à ne pas avoir un DOM avec nombre trop important d'éléments.
On peut surveiller ça également, avec MutationObserver qui va pourvoir nous renseigner en direct du nombre d'éléments ajoutés ou retirés. Et il y a de la couleur, c'est joli.
Une fois placé en bas de page en environnement de développement (mieux vaut éviter en production), ça ne consomme rien et ça rend bien service.
Petit utilitaire pour passer plusieurs attributs à la fois.
Element.prototype.setAttributes = function (object) {
for (let property in object) {
this.setAttribute(property, object[property])
}
};
Détection des points de rupture (breakpoints)
Dans cette fonction, on spécifie d'abord les points de rupture définis pour le projet et les classes CSS correspondantes.
Libre à vous d'adapter. Ou pas.
((root, w) => {
const breakpoints = [
// viewports utilisés dans le design
600, 840, 1200, 1600, 1920],
classes = [
// classes CSS pour chaque viewport
'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large'];
let viewports = [];
for (let b in breakpoints) {
let i = parseInt(b);
i === 0
? viewports.push("(max-width: " + (breakpoints[i] - 1) * .0625 + "rem)")
: viewports.push("(min-width: " + breakpoints[i - 1] * .0625 + "rem) and (max-width: " + (breakpoints[i] - 1) * .0625 + "rem)");
}
// Calcul des points de rupture en rem sur une base de root à 16px
viewports.push("(min-width: " + breakpoints[breakpoints.length - 1] * .0625 + "rem)");
for (let v of viewports) {
w.matchMedia(v).addEventListener('change', e => {
if (e.matches) {
root.setAttributes({
'data-breakpoint': v,
'data-viewport': classes[viewports.indexOf(v)]
});
}
}, {passive: true});
if (w.matchMedia(v).matches) {
root.setAttributes({
'data-breakpoint': v,
'data-viewport': classes[viewports.indexOf(v)]
});
}
}
new ResizeObserver((entries) => {
let entry = entries[0];
if (entry.contentBoxSize) {
let cbs = entry.contentBoxSize[0];
root.setAttributes({
'data-width': `${cbs.inlineSize}`,
'data-height': `${cbs.blockSize}`
});
console.info(
` Fenêtre: ${Math.floor(cbs.inlineSize)}x${Math.floor(cbs.blockSize)}\n
Breakpoint: ${root.getAttribute('data-breakpoint')} (${root.getAttribute('data-viewport')})`
);
}
}).observe(root);
})(document.documentElement, window);
Détection de l'orientation
Il n'est pas inutile non plus de connaitre l'orientation de la fenêtre.
Pour le coup, c'est simple, puisqu'il n'y a que deux options :
- portrait
- landscape
Note : si la zone d'affichage est carrée, l'orientation est égale à portrait.
((root, w) => {
for (const o of ['landscape', 'portrait']) {
w.matchMedia(`(orientation: ${o})`).addEventListener('change', e => {
e.matches && root.setAttribute('data-orientation', o);
}, {passive: true});
w.matchMedia(`(orientation: ${o})`).matches && root.setAttribute('data-orientation', o);
}
})(document.documentElement, window);
Détection des noeuds
Cette fonction place un attribut data-elements sur le root, mais ne surveille que la balise body, sinon ça fait peur !
((root, b) => {
new MutationObserver(function (mutation) {
let c = b.getElementsByTagName('*').length,
nodes = parseInt(root.dataset.elements) || 0;
if (nodes !== c) {
let color = c > 1300 ? (c < 2000 ? 'orange' : 'red') : 'limegreen';
// mise en forme jolie dans la console :-)
console.info("%c Body " + c + " noeuds ", "color:" + color + ";font-family:monospace;");
root.setAttribute('data-elements', `${c}`);
}
}).observe(b, {subtree: true, childList: true, attributes: true});
})(document.documentElement, document.body);
Affichage des data
Une fois que les fonctions sont exécutées, les informations sont disponibles de deux manières.
Dans le HTML
Sur la balise HTML, les attributs sont disponibles et utilisables, en CSS ou Javascript.
<html lang="fr" xml:lang="fr"
data-breakpoint="(min-width:100rem) and (max-width:119.9375rem)"
data-viewport="xx-large"
data-orientation="landscape"
data-elements="960"
data-width="1618"
data-height="528">
Dans la console
Affichage des informations de fenêtre et breakpoint dans la console du navigateur.
Par exemple :
body 960 noeuds
Fenêtre: 1618x914
Breakpoint: (min-width:100rem) and (max-width:119.9375rem) (xx-large)
Le bonus
Une dernière ligne qui peut servir.
const htmlDomSize = parseInt(window.getComputedStyle(document.documentElement, null).getPropertyValue("font-size")) / 16;
En posant cette constante, on peut savoir si l'utilisateur a configuré une taille de police différente de 16 pixels, qui est la base standard des navigateurs.
console.info(`Taille de police navigateur configurée à ${htmlDomSize * 16}px (${htmlDomSize}rem)`);
donnera l'information dans la console.
That's all folks !