59 lines
2.2 KiB
JavaScript
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);
|
|
}
|