
Une récente discussion sur le forum a réveillé en moi ce qui va suivre ...
Le but est de rendre les éléments HTML <area>, d'une <map>, responsive dynamiquement en s'aidant de JavaScript.
Constat
Il peut nous arriver d'avoir besoin qu'une image, sur laquelle une <map> est associée, soit responsive.
On sait que l'attribut coords, des <area>, défini les coordonnées de la forme décrite par l'attribut shape et permet ainsi de positionner et dimensionner les zones interactives d'une image.
Seulement voilà les valeurs sont exprimées en pixels CSS et ne peuvent plus forcément s'appliquer si l'image, dont elles représentent les zones d'intérêt, est elle « responsive ».
Ce que l'on souhaite obtenir
Des zones d'intérêt toujours au même endroit par rapport à l'image initiale.
• Image dimensions 600 x 336
• Image dimensions 400 x 224
• Image dimensions 200 x 112
La solution est donc de recalculer ces coordonnées dynamiquement en fonction de la taille de l'image référente.
Nota : Il faut que les zones soient définies par rapport à l'image originale non redimensionnée pour que cela ait de l’intérêt. Dans l'exemple présenté, la
<map> est calculée, volontairement, sur une image surdimensionnée de 1920 x 1080.
Code de la fonction qui fait ça
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| /**
* recalcule les coordonnées suivant les dimensions de l'image référente
* @param {(null|string)} selector - rien ou sélecteur CSS de l'image ciblée.
* @example responsiveArea('[usemap="#name-map"]');
*/
function responsiveArea(selector) {
selector = selector || "[usemap]";
const elemImage = document.querySelectorAll(selector);
elemImage.forEach((img) => {
// get dimension image
const imgW = img.naturalWidth;
const imgH = img.naturalHeight;
const nomMap = img.getAttribute("usemap");
// image non encore chargée
if (!(imgW * imgH)) {
img.addEventListener("load", () => responsiveArea(`[usemap="${nomMap}"]`));
}
// image chargée
else {
// récup. data image
const rapportX = img.width / imgW;
const rapportY = img.height / imgH;
// get la map associée
const elemMap = document.querySelector(`[name="${nomMap.replace("#","")}"]`);
// quitte si non requis
if (elemMap.hasAttribute("noresize")) return;
// get les area enfants
const elemArea = elemMap.querySelectorAll("area");
// traitement des area
elemArea.forEach((area) => {
const shape = area.getAttribute("shape");
const originalCoords = area.getAttribute("originalcoords")?.split(",");
const coords = originalCoords || area.getAttribute("coords")?.split(",");
// sauve données initiales
if (!originalCoords) {
area.setAttribute("originalcoords", coords?.join(","));
}
// Attention : pas de test fait sur la validité de l'attribut coords
// https://html.spec.whatwg.org/multipage/image-maps.html#attr-area-coords
const newCoords = [];
switch (shape) {
case "rect":
case "poly":
for (let i = 0; i < coords.length; i += 2) {
newCoords.push(+coords[i] * rapportX, +coords[i + 1] * rapportY);
}
break;
case "circle":
newCoords.push(+coords[0] * rapportX, +coords[1] * rapportY, +coords[2] * rapportX);
break;
default:
}
// affectation
area.setAttribute("coords", newCoords.join(","));
// on previent à toutes fins utiles
area.dispatchEvent(new Event("resize"));
});
// demande redim auto
if (!img._observer) {
const objObserve = new ResizeObserver(() => {
clearTimeout(img._innerTimer);
img._innerTimer = setTimeout(() => responsiveArea(`[usemap="${nomMap}"]`), 100);
});
objObserve.observe(img);
img._observer = true;
}
}
});
} |
Démo en ligne
 |
N’hésitez pas à poster vos remarques. |
Partager