<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Regioni Italiane con Province Interattive</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', 'Roboto', 'Helvetica Neue', sans-serif;
background: linear-gradient(145deg, #e2e9de 0%, #d1dbca 100%);
min-height: 100vh;
padding: 2rem 1.5rem;
display: flex;
justify-content: center;
align-items: center;
}
/* Layout a due colonne per schermi grandi: tabella a sinistra, dettaglio province a destra */
.dashboard {
max-width: 1600px;
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 1.8rem;
justify-content: center;
}
/* Tabella container */
.table-container {
flex: 2;
min-width: 320px;
background: #ffffffef;
backdrop-filter: blur(1px);
border-radius: 2rem;
box-shadow: 0 25px 45px -12px rgba(0, 0, 0, 0.35), 0 4px 12px rgba(0, 0, 0, 0.05);
overflow: hidden;
transition: all 0.2s ease;
height: fit-content;
}
/* Pannello province */
.province-panel {
flex: 1.2;
min-width: 280px;
background: white;
border-radius: 2rem;
box-shadow: 0 20px 35px -10px rgba(0, 0, 0, 0.25);
overflow: hidden;
backdrop-filter: blur(2px);
height: fit-content;
position: sticky;
top: 2rem;
}
.panel-header {
background: #1a3e2f;
color: white;
padding: 1.3rem 1.5rem;
border-bottom: 4px solid #ffbf69;
}
.panel-header h2 {
font-size: 1.5rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.panel-header h2:before {
content: "🏛️";
font-size: 1.6rem;
}
.selected-region-name {
font-size: 1rem;
background: #ffbf6933;
display: inline-block;
padding: 0.2rem 0.8rem;
border-radius: 40px;
margin-top: 8px;
font-weight: normal;
color: #ffeaac;
}
.province-list-container {
padding: 1rem 0 1rem 0;
max-height: 550px;
overflow-y: auto;
}
.province-list {
list-style: none;
margin: 0;
padding: 0;
}
.province-list li {
padding: 0.8rem 1.5rem;
border-bottom: 1px solid #ecf3e9;
display: flex;
align-items: center;
gap: 12px;
font-weight: 500;
color: #1e3a2c;
transition: all 0.1s ease;
}
.province-list li:hover {
background-color: #f7fcf4;
padding-left: 1.8rem;
}
.province-list li:before {
content: "📍";
font-size: 0.9rem;
opacity: 0.8;
}
.empty-message {
padding: 2rem 1.5rem;
text-align: center;
color: #7f8c7a;
font-style: italic;
}
.province-count {
background: #2c5a44;
color: white;
font-size: 0.7rem;
border-radius: 40px;
padding: 0.15rem 0.6rem;
margin-left: 10px;
vertical-align: middle;
}
/* Header tabella */
.table-header {
background: #1a3e2f;
padding: 1.3rem 1.8rem;
color: white;
}
.table-header h1 {
font-size: 1.7rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 10px;
}
.sub {
font-size: 0.85rem;
margin-top: 0.4rem;
opacity: 0.85;
border-left: 3px solid #ffbf69;
padding-left: 0.75rem;
}
.table-wrapper {
overflow-x: auto;
}
.regioni-table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
background-color: #ffffff;
min-width: 550px;
}
.regioni-table thead tr {
background: #2c5a44;
color: white;
}
.regioni-table th {
padding: 1rem 1rem;
font-weight: 600;
text-align: left;
}
.regioni-table td {
padding: 0.8rem 1rem;
border-bottom: 1px solid #e2e8e8;
color: #1f2e2a;
}
.regioni-table tbody tr {
cursor: pointer;
transition: background 0.15s;
}
.regioni-table tbody tr:hover {
background-color: #f1f9ee;
}
/* Riga selezionata */
.regioni-table tbody tr.selected-row {
background-color: #fffbee;
border-left: 4px solid #ffbf69;
box-shadow: inset 0 0 0 1px #ffe3a3;
}
.regioni-table tbody tr.selected-row td:first-child {
font-weight: 700;
color: #c26b00;
}
.badge-regione {
background: #eef2eb;
font-size: 0.7rem;
padding: 0.2rem 0.5rem;
border-radius: 20px;
margin-left: 6px;
}
.footer-info {
background: #eef3ec;
padding: 0.8rem 1.8rem;
font-size: 0.75rem;
color: #3b5c4a;
border-top: 1px solid #cbdcd2;
text-align: center;
}
@media (max-width: 850px) {
body {
padding: 1rem;
}
.dashboard {
flex-direction: column;
}
.province-panel {
position: static;
top: 0;
}
}
</style>
</head>
<body>
<div class="dashboard">
<!-- Tabella regioni -->
<div class="table-container">
<div class="table-header">
<h1>🇮🇹 Le 20 Regioni Italiane</h1>
<div class="sub">Clicca su una regione per visualizzare tutte le province appartenenti</div>
</div>
<div class="table-wrapper">
<table class="regioni-table" id="regioniTable">
<thead>
<tr><th>Regione</th><th>Capoluogo</th><th>Superficie (km²)</th><th>Popolazione</th><th>Zona</th></tr>
</thead>
<tbody id="regioniTableBody">
<!-- I dati verranno popolati da JS per gestire l'ID delle regioni -->
</tbody>
</table>
</div>
<div class="footer-info">
📌 Dati ISTAT / aggiornamento 2024 - Le province includono città metropolitane ed ex province. Clicca su riga per esplorare.
</div>
</div>
<!-- Pannello province dinamico -->
<div class="province-panel" id="provincePanel">
<div class="panel-header">
<h2>Province <span style="font-size:0.9rem;">& Distretti</span></h2>
<div class="selected-region-name" id="selectedRegionDisplay">Nessuna regione selezionata</div>
</div>
<div class="province-list-container" id="provinceListContainer">
<div class="empty-message">
🔍 Seleziona una regione dalla tabella per visualizzare l'elenco delle province.
</div>
</div>
</div>
</div>
<script>
// ---------- DATABASE REGIONI + PROVINCE (elenco completo per tutte le 20 regioni) ----------
// Mappa che associa il nome regione (in minuscolo per match) a un array di province.
// Dati reali basati sulla suddivisione amministrativa italiana (al 2024).
const provincePerRegione = {
"abruzzo": ["L'Aquila", "Teramo", "Pescara", "Chieti"],
"basilicata": ["Potenza", "Matera"],
"calabria": ["Reggio Calabria", "Catanzaro", "Cosenza", "Crotone", "Vibo Valentia"],
"campania": ["Napoli", "Salerno", "Caserta", "Benevento", "Avellino"],
"emilia-romagna": ["Bologna", "Modena", "Parma", "Reggio nell'Emilia", "Ferrara", "Ravenna", "Forlì-Cesena", "Piacenza", "Rimini"],
"friuli-venezia giulia": ["Udine", "Pordenone", "Trieste", "Gorizia"],
"lazio": ["Roma", "Latina", "Frosinone", "Viterbo", "Rieti"],
"liguria": ["Genova", "Savona", "Imperia", "La Spezia"],
"lombardia": ["Milano", "Brescia", "Bergamo", "Varese", "Monza e Brianza", "Como", "Lecco", "Lodi", "Mantova", "Pavia", "Sondrio", "Cremona"],
"marche": ["Ancona", "Pesaro e Urbino", "Macerata", "Ascoli Piceno", "Fermo"],
"molise": ["Campobasso", "Isernia"],
"piemonte": ["Torino", "Novara", "Cuneo", "Alessandria", "Asti", "Verbano-Cusio-Ossola", "Biella", "Vercelli"],
"puglia": ["Bari", "Lecce", "Foggia", "Barletta-Andria-Trani", "Brindisi", "Taranto"],
"sardegna": ["Cagliari", "Sassari", "Nuoro", "Oristano", "Sud Sardegna"],
"valle d'aosta": ["Aosta"], // unica provincia (senza suddivisione provinciale, ma viene rappresentata)
"toscana": ["Firenze", "Prato", "Pistoia", "Lucca", "Pisa", "Livorno", "Grosseto", "Siena", "Arezzo", "Massa-Carrara"],
"trentino-alto adige": ["Trento", "Bolzano"], // province autonome
"umbria": ["Perugia", "Terni"],
"veneto": ["Venezia", "Verona", "Padova", "Vicenza", "Treviso", "Rovigo", "Belluno"],
"sicilia": ["Palermo", "Catania", "Messina", "Siracusa", "Trapani", "Agrigento", "Caltanissetta", "Enna", "Ragusa"]
};
// Lista completa regioni con metadati (ordine alfabetico, come mostrato)
const regioniData = [
{ nome: "Abruzzo", capoluogo: "L'Aquila", superficie: "10.831", popolazione: "1.285.000", zona: "Sud" },
{ nome: "Basilicata", capoluogo: "Potenza", superficie: "9.995", popolazione: "547.000", zona: "Sud" },
{ nome: "Calabria", capoluogo: "Catanzaro", superficie: "15.222", popolazione: "1.847.000", zona: "Sud" },
{ nome: "Campania", capoluogo: "Napoli", superficie: "13.671", popolazione: "5.580.000", zona: "Sud" },
{ nome: "Emilia-Romagna", capoluogo: "Bologna", superficie: "22.453", popolazione: "4.459.000", zona: "Nord-Est" },
{ nome: "Friuli-Venezia Giulia", capoluogo: "Trieste", superficie: "7.924", popolazione: "1.197.000", zona: "Nord-Est" },
{ nome: "Lazio", capoluogo: "Roma", superficie: "17.232", popolazione: "5.714.000", zona: "Centro" },
{ nome: "Liguria", capoluogo: "Genova", superficie: "5.416", popolazione: "1.509.000", zona: "Nord-Ovest" },
{ nome: "Lombardia", capoluogo: "Milano", superficie: "23.864", popolazione: "10.020.000", zona: "Nord-Ovest" },
{ nome: "Marche", capoluogo: "Ancona", superficie: "9.401", popolazione: "1.483.000", zona: "Centro" },
{ nome: "Molise", capoluogo: "Campobasso", superficie: "4.460", popolazione: "292.000", zona: "Sud" },
{ nome: "Piemonte", capoluogo: "Torino", superficie: "25.387", popolazione: "4.241.000", zona: "Nord-Ovest" },
{ nome: "Puglia", capoluogo: "Bari", superficie: "19.541", popolazione: "3.921.000", zona: "Sud" },
{ nome: "Sardegna", capoluogo: "Cagliari", superficie: "24.090", popolazione: "1.585.000", zona: "Isole" },
{ nome: "Valle d'Aosta", capoluogo: "Aosta", superficie: "3.261", popolazione: "125.000", zona: "Nord-Ovest" },
{ nome: "Toscana", capoluogo: "Firenze", superficie: "22.987", popolazione: "3.692.000", zona: "Centro" },
{ nome: "Trentino-Alto Adige", capoluogo: "Trento", superficie: "13.607", popolazione: "1.084.000", zona: "Nord-Est" },
{ nome: "Umbria", capoluogo: "Perugia", superficie: "8.456", popolazione: "855.000", zona: "Centro" },
{ nome: "Veneto", capoluogo: "Venezia", superficie: "18.399", popolazione: "4.850.000", zona: "Nord-Est" },
{ nome: "Sicilia", capoluogo: "Palermo", superficie: "25.832", popolazione: "4.820.000", zona: "Isole" }
];
// Helper per normalizzare nomi (lowercase senza accenti/diacritici per match)
function normalizeKey(str) {
return str.toLowerCase()
.normalize("NFD").replace(/[\u0300-\u036f]/g, "") // toglie accenti
.replace(/[- ']/g, '') // rimuove apostrofi/spazi/trattini per matching pulito (es. "valle d'aosta" -> "valle daosta")
.replace(/[^a-z0-9]/g, '');
}
// Costruisce una mappa delle chiavi normalizzate -> nome originale regione per lookup veloce
const normalizedToOriginal = new Map();
regioniData.forEach(r => {
const key = normalizeKey(r.nome);
normalizedToOriginal.set(key, r.nome);
});
// Funzione per ottenere province da nome regione (gestisce sia nome esatto che fuzzy)
function getProvinceByRegioneNome(regioneNome) {
const lowerNome = regioneNome.toLowerCase();
// prima prova a cercare direttamente nelle chiavi della mappa provincePerRegione
const directKey = lowerNome;
if (provincePerRegione[directKey]) {
return provincePerRegione[directKey];
}
// prova normalizzata
const normalizedInput = normalizeKey(regioneNome);
for (let [key, provinceArray] of Object.entries(provincePerRegione)) {
const normalizedKey = normalizeKey(key);
if (normalizedKey === normalizedInput || key === lowerNome) {
return provinceArray;
}
}
// casi particolari: "Valle d'Aosta" mapping già ok, "Friuli-Venezia Giulia"
if (normalizedInput === "friuliveneziagiulia") return provincePerRegione["friuli-venezia giulia"];
if (normalizedInput === "valle daosta") return provincePerRegione["valle d'aosta"];
if (normalizedInput === "trentinoadige") return provincePerRegione["trentino-alto adige"];
return [];
}
// Render della tabella regioni
const tbody = document.getElementById('regioniTableBody');
function renderRegioniTable() {
tbody.innerHTML = '';
regioniData.forEach(reg => {
const tr = document.createElement('tr');
tr.setAttribute('data-regione', reg.nome);
// Aggiunta event listener
tr.addEventListener('click', (e) => {
e.stopPropagation();
// Rimuovi classe selected da tutte le righe
document.querySelectorAll('#regioniTableBody tr').forEach(row => row.classList.remove('selected-row'));
tr.classList.add('selected-row');
// Aggiorna pannello province con le province di questa regione
const nomeRegione = reg.nome;
updateProvincePanel(nomeRegione);
});
// celle
tr.innerHTML = `
<td><strong>${reg.nome}</strong></td>
<td>${reg.capoluogo}</td>
<td>${reg.superficie}</td>
<td>${reg.popolazione}</td>
<td>${reg.zona}</td>
`;
tbody.appendChild(tr);
});
}
// Funzione per aggiornare il pannello delle province
function updateProvincePanel(regioneNome) {
const selectedSpan = document.getElementById('selectedRegionDisplay');
const container = document.getElementById('provinceListContainer');
selectedSpan.innerHTML = `${regioneNome} <span class="province-count">province</span>`;
// Ottieni l'elenco province
let provinceList = getProvinceByRegioneNome(regioneNome);
// Se per qualche regione non trova province, array vuoto (caso mai succeda)
if (!provinceList || provinceList.length === 0) {
// fallback: alcuni casi limite come Molise o Val d'Aosta hanno array corretto. ma eventualmente mostriamo messaggio
if (regioneNome === "Molise") provinceList = ["Campobasso", "Isernia"];
else if (regioneNome === "Valle d'Aosta") provinceList = ["Aosta"];
else if (regioneNome === "Trentino-Alto Adige") provinceList = ["Trento", "Bolzano"];
else provinceList = [];
}
if (!provinceList.length) {
container.innerHTML = `<div class="empty-message">⚠️ Nessuna provincia disponibile per ${regioneNome}.<br>Dati in aggiornamento.</div>`;
return;
}
// Crea lista province
const ul = document.createElement('ul');
ul.className = 'province-list';
provinceList.forEach(prov => {
const li = document.createElement('li');
li.textContent = prov;
// Aggiunge eventuale dettaglio città metropolitana (opzionale)
if (prov === "Roma" || prov === "Milano" || prov === "Napoli" || prov === "Torino" || prov === "Firenze" || prov === "Bologna" || prov === "Venezia" || prov === "Genova" || prov === "Bari" || prov === "Catania" || prov === "Palermo") {
const span = document.createElement('span');
span.textContent = " 🌆 Città metr.";
span.style.fontSize = "0.65rem";
span.style.background = "#e9f0e5";
span.style.padding = "0.1rem 0.4rem";
span.style.borderRadius = "20px";
span.style.marginLeft = "8px";
li.appendChild(span);
}
ul.appendChild(li);
});
container.innerHTML = '';
container.appendChild(ul);
// aggiungi contatore province sotto
const counterSpan = document.createElement('div');
counterSpan.style.padding = "0.8rem 1.2rem";
counterSpan.style.borderTop = "1px solid #e2e8e2";
counterSpan.style.fontSize = "0.75rem";
counterSpan.style.color = "#466b56";
counterSpan.style.background = "#f9fdf7";
counterSpan.innerHTML = `📌 Totale province: <strong>${provinceList.length}</strong>`;
container.appendChild(counterSpan);
}
// Gestione iniziale + eventuale region pre-selezionata (opzionale, nessuna)
renderRegioniTable();
// Se cliccano su riga ma anche miglioria: se si vuole evidenziare la prima regione? Facoltativo: meglio nessuna pre-selezione
// Funzione extra per messaggio benvenuto
const initialMsg = document.getElementById('provinceListContainer');
if (initialMsg) {
// Lasciamo il messaggio di benvenuto default fino a prima interazione
}
// aggiunta stile per migliorare chiarezza e scroll fine
const style = document.createElement('style');
style.textContent = `
.province-list li {
list-style: none;
border-bottom: 1px solid #e9f0e5;
}
.province-list-container {
scrollbar-width: thin;
}
.selected-row {
background: #fff2df !important;
border-left: 4px solid #ffa559;
}
.selected-row td:first-child {
color: #b35f00;
}
.regioni-table tbody tr {
transition: all 0.1s;
}
`;
document.head.appendChild(style);
// Aggiungere funzionalità: se l'utente preme sulla prima riga dopo il caricamento? Niente, ma update al primo click.
// Eventuale highlight se l'utente torna indietro? tutto liscio.
// Per eventuale accessibilità: se clicco con tastiera? tab focus + enter
const allRows = () => document.querySelectorAll('#regioniTableBody tr');
const addKeyboardAccess = () => {
const rows = document.querySelectorAll('#regioniTableBody tr');
rows.forEach(row => {
row.setAttribute('tabindex', '0');
row.setAttribute('role', 'button');
row.setAttribute('aria-label', `Seleziona regione ${row.getAttribute('data-regione')} per vedere le province`);
row.addEventListener('keypress', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
row.click();
}
});
});
};
// Piccolo osservatore per mantenere aggiornata l'accessibilità dopo render (ma il render è solo all'inizio)
setTimeout(() => addKeyboardAccess(), 50);
// Inoltre: se l'utente vuole vedere le province della prima regione, nessuna forzatura automatica per evitare confusione.
// Possibile miglioria: mostrare una regione di default (es. Lombardia) per suggerire interattività?
// Su richiesta implicita: l'utente deve cliccare.
// Aggiungo una piccola nota nel pannello per incoraggiare il click.
const panelContainer = document.getElementById('provinceListContainer');
if(panelContainer && panelContainer.innerHTML.includes('Seleziona una regione')) {
// manteniamo
}
// Se si vuole rendere più chiaro che la tabella è interattiva aggiungo uno stile tooltip
const tableHeaderSub = document.querySelector('.sub');
if(tableHeaderSub) {
tableHeaderSub.style.cursor = "default";
}
// Correzioni finali: assicuriamoci che Valle d'Aosta e Friuli mostrino province corrette
// override manuale robusto: mappa già copre tutto, ma doppia sicurezza
window.validateProvince = () => {
// test rapido
if(getProvinceByRegioneNome("Valle d'Aosta").length === 0) {
provincePerRegione["valle d'aosta"] = ["Aosta"];
}
if(getProvinceByRegioneNome("Friuli-Venezia Giulia").length === 0) {
provincePerRegione["friuli-venezia giulia"] = ["Udine", "Pordenone", "Trieste", "Gorizia"];
}
if(getProvinceByRegioneNome("Molise").length === 0) {
provincePerRegione["molise"] = ["Campobasso", "Isernia"];
}
};
window.validateProvince();
// render finale
console.log("Ready: 20 regioni e al click si mostrano le rispettive province.");
</script>
</body>
</html>