fCC News Authors Page
Il Progetto
News Authors Page sviluppato con JavaScript vanilla, fetch API e gestione asincrona dei dati. Un'applicazione che dimostra caricamento progressivo, paginazione e gestione errori robusta per ottimizzare performance e user experience.
Source Code
- index.html
- styles.css
- script.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
authorContainer.innerHTML = '<p class="error-msg">There was an error loading the authors</p>';
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
if (authorDataArr.length <= endingIndex) {
loadMoreBtn.disabled = true;
loadMoreBtn.style.cursor = "not-allowed";
loadMoreBtn.textContent = 'No more data to load';
}
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar">
<div class="purple-divider"></div>
<p class="bio">${bio.length > 50 ? bio.slice(0, 50) + '...' : bio}</p>
<a class="author-link" href="${url}" target="_blank">${author} author page</a>
</div>
`;
});
};
loadMoreBtn.addEventListener('click', fetchMoreAuthors);
Il Progetto Preferito
È stato uno dei miei progetti freeCodeCamp preferiti! Mi ha fatto riflettere profondamente su come ottimizzare il caricamento dei contenuti per utenti con esigenze diverse.
L'Intuizione: Adaptive Loading
Mi è sorta un'intuizione che attribuisco al corso di Google UX, dove è stato spiegato come funzionano le tariffe a consumo in paesi emergenti come l'India, e di come chi usa desktop abbia molte più probabilità di avere una connessione veloce essendo più probabilmente connesso a una rete domestica (ADSL, Fibra ottica).
Il problema del tutorial:
Il tutorial freeCodeCamp carica sempre 8 autori alla volta, un compromesso fisso che non considera le diverse condizioni degli utenti. Mi sono chiesto: perché imporre questa frizione (click per caricare) a chi ha connessione velocissima? Contemporaneamente, perché rischiare di sprecare dati preziosi a chi ha tariffe a consumo?
La scoperta:
Esiste un approccio chiamato Adaptive Loading che risolve esattamente questo problema. Utilizzando navigator.connection.effectiveType (che restituisce "4g", "3g", "2g", "slow-2g") e "navigator.connection.saveData" (un booleano che indica se l'utente ha attivato il risparmio dati), si può caricare dinamicamente 5, 8 o 20 elementi in base alla connessione effettiva.
Inoltre, differenziando tra desktop e mobile (window.innerWidth), si può ottimizzare ulteriormente: desktop con WiFi domestico può caricare tutto, mobile su rete dati carica progressivamente.
La Riflessione Notturna: Lazy Loading
Questa notte (letteralmente) mi sono svegliato pensando che la soluzione migliore, semplice e che si adatta veramente a tutti sia eliminare del tutto il pulsante e caricare un po' alla volta (Lazy Loading con Infinite Scroll) il contenuto della pagina.
Credo sia la soluzione ottimale perché chi ha internet veloce non noterà il caricamento progressivo, tutto apparirà fluido, chi ha internet lento vedrà un messaggio di caricamento dopo un breve lasso di tempo, senza la frizione del click manuale, usando Intersection Observer, si caricano automaticamente i prossimi 8 autori quando l'utente arriva in fondo alla lista, garantendo zero frizioni.
Questa è la soluzione più elegante perché è passiva, infatti, si adatta all'utente senza che l'utente debba fare (o sapere) nulla.
Cosa Ho Imparato
Fetch API e Catena di Promesse:
.fetch()restituisce una Promise che risolve con un oggetto Response.then(res => res.json())è il "doppio .then()" necessario: prima spacchetti la Response, poi interpreti il JSON.catch()cattura TUTTI gli errori della catena (rete, parsing, logica)
Gestione Errori UX-Oriented:
- Errori per lo sviluppatore:
console.error()per debug tecnico - Errori per l'utente:
innerHTMLcon messaggio comprensibile - Distinzione importante: l'utente non deve vedere errori tecnici
Paginazione con Array Slicing:
startingIndexeendingIndexcome "finestra mobile" sull'array completo.slice(start, end)per estrarre "pezzi" di dati senza modificare l'originale- Incremento progressivo (
+= 8) per mostrare il prossimo batch
Caching Intelligente:
authorDataArr = datasalva TUTTI i dati dopo il primo fetch- Fetch successivi non necessari, solo slicing dell'array locale
- Riduce drasticamente le chiamate di rete
innerHTML: = vs +=:
=(assegnazione) sostituisce completamente il contenuto: ideale per errori o reset+=(concatenazione) aggiunge al contenuto esistente: ideale per loop di card- Comprensione di quando usare ciascuno è fondamentale per UX corretta
Destructuring Avanzato:
({ author, image, url, bio })spacchetta oggetti in variabili singole- Più pulito e leggibile di
obj.author,obj.imageripetuti - Pattern moderno ES6 che rende il codice più elegante
Disabilitazione UX del Button:
disabled = trueper bloccare clickstyle.cursor = "not-allowed"per feedback visivotextContentaggiornato per comunicare chiaramente lo stato
Adaptive Loading (Scoperta):
navigator.connectionAPI per rilevare tipo di connessione effettivanavigator.connection.saveDataper rispettare preferenze utente- Logica condizionale per adattare
endingIndexdinamicamente
Infinite Scroll Pattern:
- Intersection Observer per rilevare quando utente arriva in fondo
- Caricamento automatico senza frizione del click
- Messaggio di loading dopo timeout per connessioni lente
Design Responsivo Performance-Oriented:
- Desktop (schermo grande + WiFi) → carica più elementi
- Mobile (schermo piccolo + dati) → carica progressivamente
- Rispetto implicito delle condizioni hardware e rete dell'utente
Prossimo Progetto: Imparare la Programmazione Asincrona costruendo un fCC Forum Leaderboard