Files
2026-08-05 22:40:03 +02:00

59 lines
2.2 KiB
JavaScript

/**
* back-to-top.js — Bouton de retour en haut de page
* =============================================================================
* Le bouton n'apparaît qu'une fois l'utilisateur descendu au-delà d'une
* hauteur d'écran : afficher un raccourci de retour alors qu'on est déjà
* en haut n'a aucune utilité et encombre l'interface.
*
* Détection par IntersectionObserver sur une sentinelle placée en haut du
* document, plutôt que par écouteur de scroll — même raison que pour le
* scroll-spy : pas de recalcul de mise en page à chaque pixel parcouru.
*
* Le bouton est un vrai <button> injecté par ce script : il n'a aucun sens
* sans JavaScript, il ne doit donc pas exister dans le HTML servi.
*/
/**
* Initialise le bouton de retour en haut.
*
* @param {object} options
* @param {HTMLElement} options.button - Bouton à piloter.
* @param {HTMLElement} options.sentinel - Élément repère en haut de page.
* @returns {void}
*/
export function initBackToTop({ button, sentinel }) {
if (!button || !sentinel) return;
/* --- Action : remonter en haut -------------------------------------- */
button.addEventListener("click", () => {
// Respecte la préférence système de réduction des animations :
// un défilement animé sur plusieurs milliers de pixels peut
// provoquer un malaise chez les personnes photosensibles.
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
window.scrollTo({
top: 0,
behavior: prefersReducedMotion ? "auto" : "smooth",
});
// Le focus doit suivre le déplacement visuel, sinon un utilisateur
// clavier se retrouve à naviguer depuis le bas de page.
document.getElementById("haut-de-page")?.focus({ preventScroll: true });
});
/* --- Visibilité conditionnelle -------------------------------------- */
if (!("IntersectionObserver" in window)) return;
const observer = new IntersectionObserver(
([entry]) => {
// La sentinelle est visible => on est en haut => bouton masqué.
button.dataset.visible = String(!entry.isIntersecting);
},
{ threshold: 0 }
);
observer.observe(sentinel);
}