Football Team Cards
Il Progetto
Football Team Cards interattive con sistema di filtraggio avanzato, sviluppate utilizzando modern JavaScript methods, destructuring, e controllo di flusso con switch statement. Un museo digitale della squadra Argentina del 1986.
Codice Sorgente
- 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>
Learn Modern JavaScript methods by building football team cards
</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<h1 class="title">Team stats</h1>
<main>
<div class="team-stats">
<p>Team: <span id="team"></span></p>
<p>Sport: <span id="sport"></span></p>
<p>Year: <span id="year"></span></p>
<p>Head coach: <span id="head-coach"></span></p>
</div>
<label class="options-label" for="players">Filter Teammates:</label>
<select name="players" id="players">
<option value="all">All Players</option>
<option value="nickname">Nicknames</option>
<option value="forward">Position Forward</option>
<option value="midfielder">Position Midfielder</option>
<option value="defender">Position Defender</option>
<option value="goalkeeper">Position Goalkeeper</option>
</select>
<div class="cards" id="player-cards">
<div class="player-card">
<h2>Sergio Almirón</h2>
<p>Position: forward</p>
<p>Number: 1</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>Sergio Batista</h2>
<p>Position: midfielder</p>
<p>Number: 2</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>Ricardo Bochini</h2>
<p>Position: midfielder</p>
<p>Number: 3</p>
<p>Nickname: El Bocha</p>
</div>
<div class="player-card">
<h2>Claudio Borghi</h2>
<p>Position: midfielder</p>
<p>Number: 4</p>
<p>Nickname: Bichi</p>
</div>
<div class="player-card">
<h2>José Luis Brown</h2>
<p>Position: defender</p>
<p>Number: 5</p>
<p>Nickname: Tata</p>
</div>
<div class="player-card">
<h2>Daniel Passarella</h2>
<p>Position: defender</p>
<p>Number: 6</p>
<p>Nickname: El Gran Capitán</p>
</div>
<div class="player-card">
<h2>Jorge Burruchaga</h2>
<p>Position: forward</p>
<p>Number: 7</p>
<p>Nickname: Burru</p>
</div>
<div class="player-card">
<h2>Néstor Clausen</h2>
<p>Position: defender</p>
<p>Number: 8</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>José Luis Cuciuffo</h2>
<p>Position: defender</p>
<p>Number: 9</p>
<p>Nickname: El Cuchu</p>
</div>
<div class="player-card">
<h2>(Captain) Diego Maradona</h2>
<p>Position: midfielder</p>
<p>Number: 10</p>
<p>Nickname: El Pibe de Oro</p>
</div>
<div class="player-card">
<h2>Jorge Valdano</h2>
<p>Position: forward</p>
<p>Number: 11</p>
<p>Nickname: The Philosopher of Football</p>
</div>
<div class="player-card">
<h2>Héctor Enrique</h2>
<p>Position: midfielder</p>
<p>Number: 12</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>Oscar Garré</h2>
<p>Position: defender</p>
<p>Number: 13</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>Ricardo Giusti</h2>
<p>Position: midfielder</p>
<p>Number: 14</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>Luis Islas</h2>
<p>Position: goalkeeper</p>
<p>Number: 15</p>
<p>Nickname: El loco</p>
</div>
<div class="player-card">
<h2>Julio Olarticoechea</h2>
<p>Position: defender</p>
<p>Number: 16</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>Pedro Pasculli</h2>
<p>Position: forward</p>
<p>Number: 17</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>Nery Pumpido</h2>
<p>Position: goalkeeper</p>
<p>Number: 18</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>Oscar Ruggeri</h2>
<p>Position: defender</p>
<p>Number: 19</p>
<p>Nickname: El Cabezón</p>
</div>
<div class="player-card">
<h2>Carlos Tapia</h2>
<p>Position: midfielder</p>
<p>Number: 20</p>
<p>Nickname: N/A</p>
</div>
<div class="player-card">
<h2>Marcelo Trobbiani</h2>
<p>Position: midfielder</p>
<p>Number: 21</p>
<p>Nickname: Calesita</p>
</div>
<div class="player-card">
<h2>Héctor Zelada</h2>
<p>Position: goalkeeper</p>
<p>Number: 22</p>
<p>Nickname: N/A</p>
</div>
</div>
</main>
<footer>© freeCodeCamp</footer>
<script src="./script.js"></script>
</body>
</html>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--dark-grey: #0a0a23;
--light-grey: #f5f6f7;
--white: #ffffff;
--black: #000;
}
body {
background-color: var(--dark-grey);
text-align: center;
padding: 10px;
}
.title,
.options-label,
.team-stats,
footer {
color: var(--white);
}
.title {
margin: 1.3rem 0;
}
.team-stats {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
font-size: 1.3rem;
margin: 1.2rem 0;
}
.options-label {
font-size: 1.2rem;
}
.cards {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.player-card {
background-color: var(--light-grey);
padding: 1.3rem;
margin: 1.2rem;
width: 300px;
border-radius: 15px;
}
@media (max-width: 768px) {
.team-stats {
flex-direction: column;
}
}
const teamName = document.getElementById("team");
const typeOfSport = document.getElementById("sport");
const worldCupYear = document.getElementById("year");
const headCoach = document.getElementById("head-coach");
const playerCards = document.getElementById("player-cards");
const playersDropdownList = document.getElementById("players");
const myFavoriteFootballTeam = {
team: "Argentina",
sport: "Football",
year: 1986,
isWorldCupWinner: true,
headCoach: {
coachName: "Carlos Bilardo",
matches: 7,
},
players: [
{
name: "Sergio Almirón",
position: "forward",
number: 1,
isCaptain: false,
nickname: null,
},
{
name: "Sergio Batista",
position: "midfielder",
number: 2,
isCaptain: false,
nickname: null,
},
{
name: "Ricardo Bochini",
position: "midfielder",
number: 3,
isCaptain: false,
nickname: "El Bocha",
},
{
name: "Claudio Borghi",
position: "midfielder",
number: 4,
isCaptain: false,
nickname: "Bichi",
},
{
name: "José Luis Brown",
position: "defender",
number: 5,
isCaptain: false,
nickname: "Tata",
},
{
name: "Daniel Passarella",
position: "defender",
number: 6,
isCaptain: false,
nickname: "El Gran Capitán",
},
{
name: "Jorge Burruchaga",
position: "forward",
number: 7,
isCaptain: false,
nickname: "Burru",
},
{
name: "Néstor Clausen",
position: "defender",
number: 8,
isCaptain: false,
nickname: null,
},
{
name: "José Luis Cuciuffo",
position: "defender",
number: 9,
isCaptain: false,
nickname: "El Cuchu",
},
{
name: "Diego Maradona",
position: "midfielder",
number: 10,
isCaptain: true,
nickname: "El Pibe de Oro",
},
{
name: "Jorge Valdano",
position: "forward",
number: 11,
isCaptain: false,
nickname: "The Philosopher of Football",
},
{
name: "Héctor Enrique",
position: "midfielder",
number: 12,
isCaptain: false,
nickname: null,
},
{
name: "Oscar Garré",
position: "defender",
number: 13,
isCaptain: false,
nickname: null,
},
{
name: "Ricardo Giusti",
position: "midfielder",
number: 14,
isCaptain: false,
nickname: null,
},
{
name: "Luis Islas",
position: "goalkeeper",
number: 15,
isCaptain: false,
nickname: "El loco",
},
{
name: "Julio Olarticoechea",
position: "defender",
number: 16,
isCaptain: false,
nickname: null,
},
{
name: "Pedro Pasculli",
position: "forward",
number: 17,
isCaptain: false,
nickname: null,
},
{
name: "Nery Pumpido",
position: "goalkeeper",
number: 18,
isCaptain: false,
nickname: null,
},
{
name: "Oscar Ruggeri",
position: "defender",
number: 19,
isCaptain: false,
nickname: "El Cabezón",
},
{
name: "Carlos Tapia",
position: "midfielder",
number: 20,
isCaptain: false,
nickname: null,
},
{
name: "Marcelo Trobbiani",
position: "midfielder",
number: 21,
isCaptain: false,
nickname: "Calesita",
},
{
name: "Héctor Zelada",
position: "goalkeeper",
number: 22,
isCaptain: false,
nickname: null,
},
],
};
Object.freeze(myFavoriteFootballTeam);
const { sport, team, year, players } = myFavoriteFootballTeam;
const { coachName } = myFavoriteFootballTeam.headCoach;
typeOfSport.textContent = sport;
teamName.textContent = team;
worldCupYear.textContent = year;
headCoach.textContent = coachName;
const setPlayerCards = (arr = players) => {
playerCards.innerHTML += arr
.map(
({ name, position, number, isCaptain, nickname }) => {
return `
<div class="player-card">
<h2>${isCaptain ? "(Captain)" : ""} ${name}</h2>
<p>Position: ${position}</p>
<p>Number: ${number}</p>
<p>Nickname: ${nickname !== null ? nickname : "N/A"}</p>
</div>
` }
)
.join("");
};
playersDropdownList.addEventListener("change", (e) => {
playerCards.innerHTML = "";
switch (e.target.value) {
case "nickname":
setPlayerCards(players.filter((player) => player.nickname !== null));
break;
case "forward":
setPlayerCards(players.filter((player) => player.position === "forward"));
break;
case "midfielder":
setPlayerCards(
players.filter((player) => player.position === "midfielder")
);
break;
case "defender":
setPlayerCards(
players.filter((player) => player.position === "defender")
);
break;
case "goalkeeper":
setPlayerCards(
players.filter((player) => player.position === "goalkeeper")
);
break;
default:
setPlayerCards()
}
});
L'Approccio Consolidato
Ho adottato lo stesso approccio metodico utilizzato nel progetto precedente. Ora ho già pronti nuovi paragrafi strutturati da aggiungere al vademecum. A questo punto è come se avessi sviluppato due vademecum paralleli, quindi è finalmente arrivato il momento di unificare e integrare tutti questi nuovi concetti nel vademecum principale!
L'Approfondimento dello Switch Statement
Non mi è dispiaciuto questo progetto e soprattutto approfondire l'istruzione di controllo switch. Credevo di aver raggiunto l'apice della leggibilità del codice con le istruzioni condizionali if/else, ma switch forse le supera. Ho visto però che non vanno usate "a piacere", hanno delle particolarità ben specifiche che questa piccola tabella riassume:
Tabella di Confronto: if...else if vs switch
| Caratteristica | if... else if... else | switch |
|---|---|---|
| Tipo di Confronto | Flessibile: Può valutare condizioni complesse e diverse per ogni blocco (es. x > 10, y === 'test', z <= 5). | Rigido: Confronta una singola variabile/espressione con una serie di valori costanti (es. x === 1, x === 'a'). |
| Leggibilità | Meno leggibile quando ci sono molte condizioni simili. La variabile da controllare viene ripetuta in ogni if. | Molto più leggibile e pulito quando si confronta una singola variabile con molti valori possibili. L'intento è più chiaro. |
| Casi d'Uso Ideali | Quando hai bisogno di controllare range di valori (es. punteggio > 90) o condizioni multiple e non correlate. | Quando hai una lista di valori specifici e discreti da controllare (es. giorni della settimana, opzioni di un menu, codici di stato). |
| Gestione "Default" | Gestita dall'ultimo blocco else, che cattura tutti i casi non coperti. | Gestita dal blocco default, che ha lo stesso scopo ed è altamente raccomandato per la robustezza. |
| Performance | Generalmente considerato leggermente più lento dai motori JavaScript moderni quando ci sono molte condizioni, ma la differenza è quasi sempre trascurabile. | In molti scenari, può essere leggermente più veloce perché i motori JavaScript possono ottimizzarlo meglio (es. usando una jump table), ma non dovrebbe essere il motivo principale della scelta. |
| Rischio "Fall-through" | Non esiste. Ogni blocco è isolato. | Esiste se si dimentica l'istruzione break. Questo può essere sia un bug che una feature avanzata (per raggruppare casi). |
Cosa Ho Imparato
Destructuring JavaScript Moderno:
- Destructuring degli oggetti con
const { sport, team, year, players } = myFavoriteFootballTeam - Destructuring di oggetti annidati con
const { coachName } = myFavoriteFootballTeam.headCoach - Destructuring degli array nei parametri delle funzioni
Padronanza dello Switch Statement:
- Istruzione switch per gestire condizioni multiple
- Istruzioni break per evitare il comportamento fall-through
- Case default per gestione robusta degli errori
- Switch sui valori degli eventi con
e.target.value
Metodi Array Avanzati:
.filter()per filtraggio condizionale degli array.map()per trasformazione degli array in HTML- Concatenazione di metodi per operazioni consecutive
Metodi degli Oggetti:
Object.freeze()per creare oggetti immutabili- Accesso alle proprietà degli oggetti con dot notation
- Navigazione di oggetti annidati complessi
Template Literals e Rendering Condizionale:
- Template literals avanzati con logica condizionale
- Operatori ternari per contenuti condizionali (
isCaptain ? "(Captain)" : "") - Controllo dei valori null con rendering condizionale (
nickname !== null ? nickname : "N/A")
Gestione Eventi e Manipolazione DOM:
- Event listener con destructuring dell'oggetto evento
- Pulizia dinamica del contenuto con
innerHTML = "" - Chiamate di funzioni con parametri opzionali
Funzionalità ES6+ Moderne:
- Arrow functions per sintassi concisa
- Template literals per generazione HTML
- Const/let per scoping corretto delle variabili
Riflessione
Non sono un amante del calcio, ma ammetto che l'idea di freeCodeCamp di creare un museo digitale dedicato ai leggendari giocatori del passato attraverso il codice mi ha emozionato.
Prossimo Progetto: Imparare localStorage costruendo una Todo App