Blog: cambio categoria e pagina senza ricaricare tutto
Ogni clic su una categoria ricaricava l'intera pagina, header e hero compresi, con lo sfarfallio che ne consegue. Ora si scarica la stessa pagina e si sostituiscono soltanto la griglia e i filtri, con una breve dissolvenza; l'indirizzo resta corretto e avanti/indietro funzionano. I link restano link veri: senza JavaScript, o se la risposta non ha la forma attesa, la navigazione avviene come prima. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,8 +38,9 @@ const linkFor = (cat?: string, p = 1) => {
|
||||
<a class:list={['bfilters__item', { 'is-active': category === c }]} href={linkFor(c)}>{c}</a>
|
||||
))}
|
||||
</nav>
|
||||
<div id="blog-results">
|
||||
{items.length === 0 ? (
|
||||
<p style="text-align:center">Nessun articolo pubblicato{category ? ' in questa categoria' : ''}, torna a trovarci presto.</p>
|
||||
<p class="bempty">Nessun articolo pubblicato{category ? ' in questa categoria' : ''}, torna a trovarci presto.</p>
|
||||
) : (
|
||||
<div class="bgrid">
|
||||
{items.map((post) => (
|
||||
@@ -61,13 +62,70 @@ const linkFor = (cat?: string, p = 1) => {
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Base>
|
||||
|
||||
<script>
|
||||
// Cambio categoria e pagina senza ricaricare: si scarica la stessa pagina e si sostituiscono
|
||||
// solo la griglia e i filtri. I link restano normali, quindi senza JS tutto continua a
|
||||
// funzionare come prima.
|
||||
const risultati = document.getElementById('blog-results');
|
||||
const filtri = document.querySelector('.bfilters');
|
||||
|
||||
if (risultati && filtri) {
|
||||
let inCorso: AbortController | null = null;
|
||||
|
||||
async function mostra(href: string, scorri: boolean): Promise<void> {
|
||||
inCorso?.abort();
|
||||
const controller = new AbortController();
|
||||
inCorso = controller;
|
||||
risultati!.classList.add('is-loading');
|
||||
try {
|
||||
const res = await fetch(href, { signal: controller.signal });
|
||||
const doc = new DOMParser().parseFromString(await res.text(), 'text/html');
|
||||
const nuoviRisultati = doc.getElementById('blog-results');
|
||||
const nuoviFiltri = doc.querySelector('.bfilters');
|
||||
// Se la pagina non ha la forma attesa (errore, redirect) si lascia fare al browser.
|
||||
if (!nuoviRisultati || !nuoviFiltri) { location.href = href; return; }
|
||||
risultati!.innerHTML = nuoviRisultati.innerHTML;
|
||||
filtri!.innerHTML = nuoviFiltri.innerHTML;
|
||||
if (scorri) filtri!.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
} catch (e) {
|
||||
if ((e as Error).name === 'AbortError') return;
|
||||
location.href = href;
|
||||
return;
|
||||
} finally {
|
||||
if (inCorso === controller) {
|
||||
risultati!.classList.remove('is-loading');
|
||||
inCorso = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delega sul documento: i link vengono rigenerati a ogni cambio, così non serve riagganciarli.
|
||||
document.addEventListener('click', (e) => {
|
||||
const link = (e.target as Element).closest<HTMLAnchorElement>('.bfilters__item, .bpages__n');
|
||||
if (!link || e.metaKey || e.ctrlKey || e.shiftKey || (e as MouseEvent).button !== 0) return;
|
||||
e.preventDefault();
|
||||
if (link.classList.contains('is-active')) return;
|
||||
history.pushState({}, '', link.href);
|
||||
mostra(link.href, link.classList.contains('bpages__n'));
|
||||
});
|
||||
|
||||
// Avanti/indietro del browser: si ricarica il contenuto senza toccare la cronologia.
|
||||
addEventListener('popstate', () => mostra(location.href, false));
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.bfilters { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-bottom: 50px; }
|
||||
.bfilters__item { font-family: var(--font-heading); font-size: .7rem; letter-spacing: .16em; text-transform: uppercase; text-decoration: none; color: var(--c-text); padding: 10px 16px; }
|
||||
.bfilters__item.is-active { background: var(--c-accent); color: #fff; }
|
||||
.bfilters__item { transition: background .2s ease, color .2s ease; }
|
||||
#blog-results { transition: opacity .25s ease; }
|
||||
#blog-results.is-loading { opacity: .35; }
|
||||
.bempty { text-align: center; }
|
||||
.bgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 50px 40px; }
|
||||
.bcard img { aspect-ratio: 4/3; object-fit: cover; width: 100%; margin-bottom: 14px; }
|
||||
.bcard__date { font-size: .78rem; color: var(--c-text-light); margin-bottom: 4px; }
|
||||
|
||||
Reference in New Issue
Block a user