Scrolling in JavaScript
Gli eventi di scrolling in JavaScript permettono di reagire allo scorrimento della pagina, utili per lazy loading e animazioni.
Capire gli eventi e le tecniche di scrolling in JavaScript
Gli eventi di scrolling in JavaScript ti permettono di reagire alla posizione dell'utente sulla pagina. Questa è la base per funzionalità come gli effetti parallax, l'attivazione di animazioni in base alla posizione di scorrimento, l'implementazione dello scroll infinito, i pulsanti "torna in cima", le intestazioni fisse e le barre di avanzamento della lettura. Questa guida spiega come leggere la posizione di scorrimento corrente, come scorrere in modo programmatico, come gestire l'evento scroll in modo efficiente e quando utilizzare il moderno IntersectionObserver.
Leggere la posizione di scorrimento corrente
Prima di reagire allo scrolling, di solito è necessario sapere di quanto è scorsa la pagina. Le proprietà più affidabili si trovano sull'oggetto window:
window.scrollY— distanza di scorrimento verticale in pixel (anchewindow.pageYOffset, un alias più vecchio).window.scrollX— distanza di scorrimento orizzontale in pixel (aliaswindow.pageXOffset).
Per un singolo elemento scorrevole, usa invece element.scrollTop e element.scrollLeft. Questi valori sono in lettura/scrittura: assegnando un valore l'elemento viene portato direttamente a quella posizione.
// How far down the whole page has the user scrolled?
console.log(window.scrollY); // e.g. 0 at the top, 420 partway down
// Total scrollable height of the document
const docHeight = document.documentElement.scrollHeight;
const winHeight = window.innerHeight;
// How close to the bottom are we (0 = top, 1 = bottom)?
const progress = window.scrollY / (docHeight - winHeight);
console.log(progress);Per gli offset relativi agli elementi e un approfondimento su getBoundingClientRect, consulta JavaScript Coordinates e Window Sizes and Scrolling.
Scorrere in modo programmatico
Non si ascolta solo lo scrolling — lo si può anche attivare. Questi metodi accettano l'opzione behavior: 'smooth' per una transizione animata invece di un salto istantaneo:
window.scrollTo(x, y)— scorre fino a una posizione assoluta.window.scrollBy(dx, dy)— scorre di una quantità relativa rispetto alla posizione corrente.element.scrollIntoView(options)— scorre in modo che un elemento specifico sia visibile.
// Jump to the very top, smoothly
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
// Nudge down by one viewport height
window.scrollBy({ top: window.innerHeight, behavior: 'smooth' });
// Bring an element into view (e.g. after navigating to an anchor)
document.querySelector('#section-2')
.scrollIntoView({ behavior: 'smooth', block: 'start' });Questo è il modo corretto per implementare un pulsante "torna in cima" o una navigazione fluida verso ancore nella pagina. Usa querySelector per ottenere prima l'elemento di destinazione.
L'evento scroll in JavaScript
L'evento scroll si attiva quando viene scrollata la visualizzazione del documento o un elemento scorrevole. È uno degli eventi più utilizzati per design dinamici e interattivi.
Concetti chiave
- Frequenza degli eventi: L'evento
scrollpuò attivarsi decine di volte al secondo, quindi il suo handler viene eseguito molto spesso. Eseguire operazioni pesanti (letture del layout, scritture nel DOM, chiamate di rete) a ogni attivazione causa uno scrolling discontinuo. La soluzione standard è throttle o debounce dell'handler. - Scrolling su
windowvs. elemento: Puoi ascoltare lo scroll sull'intera finestra (window.addEventListener('scroll', ...)) o su un elemento specifico con overflow scorrevole (el.addEventListener('scroll', ...)). scrollnon fa bubble: Un eventoscrollsu un elemento non risale al documento, quindi collega il listener all'elemento che scorre effettivamente.
Throttle vs. Debounce
Entrambi limitano la frequenza di esecuzione del tuo handler, ma si comportano in modo diverso:
- Debounce aspetta che lo scrolling si fermi per
waitms, poi esegue una sola volta. Ideale per "fare qualcosa quando l'utente finisce di scorrere" (es. salvare la posizione di scorrimento). - Throttle esegue al massimo una volta ogni
waitms durante lo scrolling. Migliore per effetti continui come le barre di avanzamento, dove si desiderano aggiornamenti regolari durante lo scorrimento.
function debounce(func, wait) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
function throttle(func, wait) {
let last = 0;
return function (...args) {
const now = Date.now();
if (now - last >= wait) {
last = now;
func.apply(this, args);
}
};
}
// Debounce: runs once 100ms after scrolling stops
window.addEventListener('scroll', debounce(() => {
// handle scroll logic here
}, 100));
// Throttle: runs at most every 100ms while scrolling
window.addEventListener('scroll', throttle(() => {
// update a progress bar, etc.
}, 100));Entrambi gli helper basati su setTimeout fanno uso delle API di scheduling setTimeout / setInterval.
Esempi pratici di gestione degli eventi scroll
Esempio 1: Mostra/nascondi la navigazione allo scroll
Questo esempio mostra come nascondere una barra di navigazione quando si scorre verso il basso e mostrarla di nuovo quando si scorre verso l'alto, un pattern comune in molti siti moderni per massimizzare lo spazio sullo schermo.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Scroll Event Navigation Example</title>
<style>
#navbar {
position: fixed;
top: 0;
width: 100%;
background-color: #333;
color: white;
text-align: center;
padding: 10px;
transition: top 0.3s;
}
body {
padding: 0;
margin: 0;
height: 1500px; /* to ensure scrolling */
font-family: Arial, sans-serif;
}
</style>
</head>
<body>
<p style="display: flex; justify-content: center; align-items: center; margin-top: 50vh;"><strong>When you scroll down, the navigation bar disappears. Scroll back up, and it reappears!</strong></p>
<div id="navbar">Navigation Bar</div>
<script>
let lastScrollTop = 0;
window.addEventListener(
"scroll",
function () {
let currentScroll = window.pageYOffset || document.documentElement.scrollTop;
if (currentScroll > lastScrollTop) {
document.getElementById("navbar").style.top = "-50px"; // Adjust based on nav height
} else {
document.getElementById("navbar").style.top = "0px";
}
lastScrollTop = currentScroll <= 0 ? 0 : currentScroll; // For Mobile or negative scrolling
},
false
);
</script>
</body>
</html>Spiegazione:
- Lo script tiene traccia dell'ultima posizione di scorrimento e la confronta con quella corrente. Se la posizione corrente è più alta, significa che l'utente sta scorrendo verso il basso, quindi la barra di navigazione viene nascosta spostando la sua posizione
topfuori dallo schermo. - Scorrendo verso l'alto, la barra di navigazione riappare.
Esempio 2: Attivazione di animazioni allo scroll
Questo esempio mostra come attivare animazioni quando gli elementi entrano nel viewport durante lo scrolling.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Scroll Animation Trigger</title>
<style>
.box {
width: 100px;
height: 100px;
background: red;
opacity: 0;
transition: opacity 2s;
margin: 600px auto; /* Ensures it starts out of view */
}
</style>
</head>
<body>
<p>Keep scrolling down to see the animation!</p>
<div class="box"></div>
<script>
let hasAnimated = false;
window.addEventListener('scroll', function() {
const box = document.querySelector('.box');
const rect = box.getBoundingClientRect();
if (rect.top < window.innerHeight && !hasAnimated) {
box.style.opacity = 1; // Fade in the box when it comes into view
hasAnimated = true;
}
});
</script>
</body>
</html>Spiegazione:
- Controllo visibilità: Lo script verifica se il bordo superiore dell'elemento
.boxè all'interno del viewport e cambia la sua opacità a 1, attivando un effetto di dissolvenza in entrata. Un flag impedisce che l'animazione si attivi nuovamente agli eventi scroll successivi.
Esempio 3: Effetto parallax allo scroll
Questo esempio dimostra un semplice effetto parallax in cui l'immagine di sfondo si muove a una velocità diversa rispetto al contenuto in primo piano mentre si scorre la pagina.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Enhanced Parallax Scrolling</title>
<style>
body, html {
height: 100%;
margin: 0;
font-family: Arial, sans-serif;
overflow-x: hidden; /* Prevent horizontal scroll */
}
.parallax {
height: 100vh; /* Full height of the viewport */
position: relative;
background: url('https://via.placeholder.com/1920x1080') no-repeat center center;
background-size: cover;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 36px;
letter-spacing: 1px;
}
.content {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: white;
color: #333;
font-size: 24px;
padding: 0 20px;
text-align: center;
box-sizing: border-box;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="content">Scroll down to see the parallax effect!</div>
<div class="parallax">Stunning Parallax!</div>
<div class="content">Keep scrolling to see more effects.</div>
<div class="parallax"></div>
<div class="content">You have reached the end. Amazing, right?</div>
<script>
document.addEventListener('scroll', function() {
document.querySelectorAll('.parallax').forEach(function(el) {
const factor = 0.5; // Change this for more or less parallax
const offset = window.pageYOffset * factor - 300; // Adjusts the starting position of background
el.style.backgroundPositionY = offset + 'px';
});
});
</script>
</body>
</html>Spiegazione:
- Stili CSS: La classe
.parallaximposta l'immagine di sfondo in modo che riempia il contenitore e la centra. L'esempio si affida interamente a JavaScript per il posizionamento, evitando il CSSbackground-attachment: fixed, che può causare problemi di prestazioni sui dispositivi mobili. - Funzionalità JavaScript: Allo scroll, lo script calcola una nuova posizione verticale per l'immagine di sfondo a partire dall'offset di scorrimento. Regolando dinamicamente
backgroundPositionY, l'immagine si sposta a una velocità diversa rispetto al contenuto della pagina, creando l'effetto di profondità parallax.
In sintesi, man mano che si scorre, le immagini di sfondo si muovono più lentamente del testo, facendo sembrare che si trovino a una profondità diversa.
L'alternativa moderna: IntersectionObserver
Per il caso comune di "fare qualcosa quando un elemento diventa visibile" (lazy loading, animazioni di dissolvenza, scroll infinito), IntersectionObserver è l'approccio moderno consigliato. Invece di eseguire il codice a ogni evento scroll e leggere manualmente le posizioni, il browser ti notifica in modo asincrono quando un elemento supera una soglia — molto più efficiente e privo di scatti.
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('visible'); // run once it enters view
observer.unobserve(entry.target); // stop watching it
}
});
},
{ threshold: 0.25 } // fire when 25% of the element is visible
);
document.querySelectorAll('.box').forEach((el) => observer.observe(el));Usa l'evento scroll quando hai bisogno di una lettura continua della posizione di scorrimento (parallax, barre di avanzamento). Usa IntersectionObserver quando ti interessa solo che un elemento sia entrato o uscito dal viewport. È concettualmente simile all'API MutationObserver.
Impedire e ripristinare lo scroll
Un requisito comune dell'interfaccia utente è bloccare lo scroll della pagina mentre un modale o un menu è aperto. Non è possibile annullare in modo affidabile l'evento scroll con preventDefault() (si attiva dopo che lo scorrimento è già avvenuto). Invece, è sufficiente alternare il CSS overflow:
// Lock scrolling (e.g. when opening a modal)
document.body.style.overflow = 'hidden';
// Restore it when the modal closes
document.body.style.overflow = '';Conclusione
Gestire gli eventi scroll in modo efficace è una competenza fondamentale per gli sviluppatori web, che consente di creare siti più interattivi e ottimizzati per le prestazioni. Che si stia implementando miglioramenti all'interfaccia utente come barre di navigazione dinamiche o effetti parallax sulla pagina, comprendere e gestire correttamente lo scrolling in JavaScript può migliorare drasticamente l'esperienza utente.