// Le code JavaScript commence ici, il est autonome et ne dépend pas d’éléments extérieurs au bloc HTML fourni.
// Aucune API externe n’est utilisée pour ce tableau, car les données comparatives sont fournies directement
// et représentent des moyennes historiques spécifiques au mois de janvier, n’étant pas dynamiques.
//
// Si des données météorologiques en temps réel ou des prévisions dynamiques étaient nécessaires,
// une API gratuite et sans clé telle que Open-Meteo (https://open-meteo.com/) pourrait être envisagée.
//
// Exemple d’utilisation d’Open-Meteo pour des données météorologiques historiques (moyennes journalières) :
// URL d’exemple (moyennes de température max et min pour Paris en janvier 2023) :
// `https://archive-api.open-meteo.com/v1/archive?latitude=48.85&longitude=2.35&start_date=2023-01-01&end_date=2023-01-31&daily=temperature_2m_max,temperature_2m_min&timezone=Europe%2FBerlin`
//
// Réponse JSON (extrait pour démonstration, le tableau ci-dessus n’en fait pas usage) :
/*
{
“latitude”: 48.85,
“longitude”: 2.35,
“generationtime_ms”: 0.5470752716064453,
“utc_offset_seconds”: 3600,
“timezone”: “Europe/Berlin”,
“timezone_abbreviation”: “CET”,
“elevation”: 42.0,
“daily_units”: {
“time”: “iso8601”,
“temperature_2m_max”: “°C”,
“temperature_2m_min”: “°C”
},
“daily”: {
“time”: [“2023-01-01”, …, “2023-01-31”],
“temperature_2m_max”: [8.4, …, 7.1],
“temperature_2m_min”: [3.6, …, -0.2]
}
}
*/
// Référence à l’élément conteneur principal du comparateur
const comparateurElement = document.getElementById(‘comparateurMajorqueJanvier’);
// Les données brutes du tableau, structurées en tableau d’objets pour faciliter la manipulation.
// Chaque objet représente une caractéristique avec ses valeurs pour chaque destination,
// et un ‘type’ pour le filtrage par catégorie.
const rawData = [
{ caracteristique: “Température moyenne”, majorque: “12°C”, paris: “5°C”, lyon: “4°C”, type: “température” },
{ caracteristique: “Température maximale”, majorque: “15-17°C”, paris: “7°C”, lyon: “6°C”, type: “température” },
{ caracteristique: “Température minimale”, majorque: “8°C”, paris: “2°C”, lyon: “1°C”, type: “température” },
{ caracteristique: “Ensoleillement moyen”, majorque: “6 heures/jour”, paris: “2 heures/jour”, lyon: “2 heures/jour”, type: “ensoleillement” },
{ caracteristique: “Jours de pluie”, majorque: “6 jours”, paris: “10 jours”, lyon: “9 jours”, type: “précipitations” },
{ caracteristique: “Sensations”, majorque: “Douceur printanière”, paris: “Froid et grisaille”, lyon: “Froid et humide”, type: “ressenti” }
];
// Fonction utilitaire pour extraire une valeur numérique d’une chaîne.
// Gère les nombres simples (“12°C” -> 12) et les plages (“15-17°C” -> 16).
function parseNumericValue(value) {
if (typeof value !== ‘string’) return value;
const matches = value.match(/(d+)(?:-(d+))?/); // Capture un ou deux nombres
if (matches) {
const num1 = parseFloat(matches[1]);
if (matches[2]) {
const num2 = parseFloat(matches[2]);
return (num1 + num2) / 2; // Retourne la moyenne pour les plages de valeurs
}
return num1;
}
return NaN; // Retourne NaN si aucune valeur numérique n’est trouvée
}
// Variables d’état pour le tri et le filtrage
let currentSortKey = null; // Clé de la colonne actuellement triée
let sortDirection = ‘asc’; // Direction du tri : ‘asc’ (ascendant) ou ‘desc’ (descendant)
let activeFilters = []; // Liste des types de filtres actifs (ex: [‘température’, ‘ensoleillement’])
let currentSearchTerm = ”; // Terme de recherche actuel pour le filtrage textuel
// Récupération des éléments DOM nécessaires
const tableBody = comparateurElement.querySelector(‘#tableBody’);
const filterInput = comparateurElement.querySelector(‘#filterInput’);
const filterBadgesContainer = comparateurElement.querySelector(‘#filterBadges’);
const resetFiltersBtn = comparateurElement.querySelector(‘#resetFiltersBtn’);
const tableHeaders = comparateurElement.querySelectorAll(‘th[data-sort-key]’); // En-têtes cliquables du tableau
// Détermine tous les types de filtres uniques disponibles à partir des données brutes
const availableFilterTypes = […new Set(rawData.map(item => item.type))];
// Génère et affiche les badges de filtre dans le DOM
function generateFilterBadges() {
filterBadgesContainer.innerHTML = ”; // Vide le conteneur avant de générer de nouveaux badges
availableFilterTypes.forEach(type => {
const badge = document.createElement(‘span’);
badge.classList.add(‘filter-badge’);
// Met la première lettre du type en majuscule pour l’affichage
badge.textContent = type.charAt(0).toUpperCase() + type.slice(1);
if (activeFilters.includes(type)) {
badge.classList.add(‘active’); // Ajoute la classe ‘active’ si le filtre est actif
}
badge.addEventListener(‘click’, () => toggleFilter(type)); // Ajoute l’écouteur de clic
filterBadgesContainer.appendChild(badge);
});
}
// Active ou désactive un filtre spécifique et met à jour le tableau
function toggleFilter(type) {
const index = activeFilters.indexOf(type);
if (index > -1) {
activeFilters.splice(index, 1); // Retire le filtre s’il était déjà actif
} else {
activeFilters.push(type); // Ajoute le filtre s’il n’était pas actif
}
generateFilterBadges(); // Re-génère les badges pour refléter le changement d’état
renderTable(); // Re-rend le tableau avec les filtres mis à jour
}
// Fonction principale pour rendre (afficher) le tableau avec les données filtrées et triées
function renderTable() {
let filteredData = […rawData]; // Commence avec une copie des données brutes
// 1. Application du filtre de recherche par mot-clé
if (currentSearchTerm) {
const searchTermLower = currentSearchTerm.toLowerCase();
filteredData = filteredData.filter(item =>
// Recherche dans les caractéristiques et les valeurs des villes
item.caracteristique.toLowerCase().includes(searchTermLower) ||
item.majorque.toLowerCase().includes(searchTermLower) ||
item.paris.toLowerCase().includes(searchTermLower) ||
item.lyon.toLowerCase().includes(searchTermLower)
);
}
// 2. Application des filtres par type (badges)
if (activeFilters.length > 0) {
filteredData = filteredData.filter(item => activeFilters.includes(item.type));
}
// 3. Application du tri
if (currentSortKey) {
filteredData.sort((a, b) => {
const valA = a[currentSortKey];
const valB = b[currentSortKey];
let comparison = 0;
// Tente de parser les valeurs numériques pour un tri correct
const numA = parseNumericValue(valA);
const numB = parseNumericValue(valB);
if (!isNaN(numA) && !isNaN(numB)) {
// Si les deux valeurs sont numériques, compare les nombres
comparison = numA – numB;
} else {
// Sinon, compare les chaînes de caractères (utile pour “Sensations” ou “Caractéristique”)
comparison = String(valA).localeCompare(String(valB));
}
// Applique la direction de tri actuelle
return sortDirection === ‘asc’ ? comparison : -comparison;
});
}
tableBody.innerHTML = ”; // Vide le corps du tableau avant d’insérer les nouvelles lignes
// Affiche un message si aucun résultat n’est trouvé après filtrage/tri
if (filteredData.length === 0) {
const noResultsRow = document.createElement(‘tr’);
noResultsRow.innerHTML = `
Aucun résultat trouvé pour votre recherche ou vos filtres.`;
tableBody.appendChild(noResultsRow);
return;
}
// Génération des lignes du tableau
filteredData.forEach(item => {
const row = document.createElement(‘tr’);
// Initialisation des classes de cellule pour la mise en avant visuelle
let majorqueCellClass = ”;
let parisCellClass = ”;
let lyonCellClass = ”;
// Applique des classes visuelles pour indiquer les “meilleures” valeurs.
// Vert clair pour les températures et ensoleillement plus élevés, et jours de pluie moins nombreux.
if (item.type === “température” || item.type === “ensoleillement”) {
const valMaj = parseNumericValue(item.majorque);
const valPar = parseNumericValue(item.paris);
const valLyo = parseNumericValue(item.lyon);
// Filtrer les NaN avant de trouver le max pour éviter Math.max(NaN)
const values = [valMaj, valPar, valLyo].filter(v => !isNaN(v));
if (values.length > 0) {
const maxVal = Math.max(…values);
if (valMaj === maxVal && !isNaN(valMaj)) majorqueCellClass += ‘ bg-green-50 text-green-700 font-semibold’;
if (valPar === maxVal && !isNaN(valPar)) parisCellClass += ‘ bg-green-50 text-green-700 font-semibold’;
if (valLyo === maxVal && !isNaN(valLyo)) lyonCellClass += ‘ bg-green-50 text-green-700 font-semibold’;
}
} else if (item.type === “précipitations”) { // Pour les jours de pluie, le plus bas est le “meilleur”
const valMaj = parseNumericValue(item.majorque);
const valPar = parseNumericValue(item.paris);
const valLyo = parseNumericValue(item.lyon);
const values = [valMaj, valPar, valLyo].filter(v => !isNaN(v));
if (values.length > 0) {
const minVal = Math.min(…values);
if (valMaj === minVal && !isNaN(valMaj)) majorqueCellClass += ‘ bg-green-50 text-green-700 font-semibold’;
if (valPar === minVal && !isNaN(valPar)) parisCellClass += ‘ bg-green-50 text-green-700 font-semibold’;
if (valLyo === minVal && !isNaN(valLyo)) lyonCellClass += ‘ bg-green-50 text-green-700 font-semibold’;
}
}
// Construit le HTML de la ligne avec les données et les classes de style
row.innerHTML = `
${item.caracteristique}
${item.majorque}
${item.paris}
${item.lyon}
`;
tableBody.appendChild(row);
});
}
// Ajoute des écouteurs d’événements pour le tri par clic sur les en-têtes du tableau
tableHeaders.forEach(header => {
header.addEventListener(‘click’, () => {
const sortKey = header.dataset.sortKey; // Récupère la clé de tri de l’attribut data-sort-key
// Réinitialise les classes de tri sur tous les en-têtes
tableHeaders.forEach(h => {
h.classList.remove(‘sort-asc’, ‘sort-desc’);
});
// Logique pour alterner la direction de tri ou changer de colonne à trier
if (currentSortKey === sortKey) {
sortDirection = sortDirection === ‘asc’ ? ‘desc’ : ‘asc’;
} else {
currentSortKey = sortKey;
sortDirection = ‘asc’; // Par défaut, tri ascendant pour une nouvelle colonne
}
// Ajoute la classe de tri actuelle à l’en-tête cliqué pour l’affichage de la flèche
header.classList.add(`sort-${sortDirection}`);
renderTable(); // Re-rend le tableau avec le nouveau tri
});
});
// Ajoute un écouteur d’événement pour la recherche par mot-clé (input en temps réel)
filterInput.addEventListener(‘input’, (event) => {
currentSearchTerm = event.target.value.trim(); // Met à jour le terme de recherche
renderTable(); // Re-rend le tableau
});
// Ajoute un écouteur d’événement pour le bouton de réinitialisation
resetFiltersBtn.addEventListener(‘click’, () => {
// Réinitialise toutes les variables d’état
currentSortKey = null;
sortDirection = ‘asc’;
activeFilters = [];
currentSearchTerm = ”;
filterInput.value = ”; // Vide le champ de recherche
// Réinitialise les classes de tri sur les en-têtes
tableHeaders.forEach(h => {
h.classList.remove(‘sort-asc’, ‘sort-desc’);
});
generateFilterBadges(); // Re-génère les badges pour désactiver tous les filtres
renderTable(); // Re-rend le tableau dans son état initial
});
// Initialisation du comparateur au chargement de la page
generateFilterBadges(); // Affiche les badges de filtre
renderTable(); // Affiche le tableau initial
{“@context”:”https://schema.org”,”@type”:”FAQPage”,”mainEntity”:[{“@type”:”Question”,”name”:”Quel est le meilleur moment pour visiter Majorque en janvier ? ud83eudd14″,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Le meilleur moment pour visiter Majorque en janvier est gu00e9nu00e9ralement la deuxiu00e8me moitiu00e9 du mois, car les tempu00e9ratures ont tendance u00e0 u00eatre lu00e9gu00e8rement plus douces et l’u00eele commence u00e0 s’animer avec des festivitu00e9s locales importantes comme Sant Antoni (17 janvier) et Sant Sebastiu00e0 (20 janvier) u00e0 Palma. Ces u00e9vu00e9nements offrent une immersion culturelle unique, ajoutant une dimension festive u00e0 votre su00e9jour.”}},{“@type”:”Question”,”name”:”Peut-on se baigner u00e0 Majorque en janvier ? ud83eudd76″,”acceptedAnswer”:{“@type”:”Answer”,”text”:”La tempu00e9rature de la mer u00e0 Majorque en janvier est d’environ 15u00b0C, ce qui est gu00e9nu00e9ralement considu00e9ru00e9 comme trop froid pour la baignade pour la plupart des gens. Cependant, les plages sont parfaites pour de longues promenades vivifiantes et la contemplation des paysages marins du00e9serts. Certains hu00f4tels proposent des piscines intu00e9rieures chauffu00e9es si vous souhaitez nager.”}},{“@type”:”Question”,”name”:”Quelles activitu00e9s sont recommandu00e9es u00e0 Majorque en janvier ? ud83cudfdeufe0f”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”En janvier, Majorque est idu00e9ale pour la randonnu00e9e dans la Serra de Tramuntana, l’exploration culturelle de Palma et de ses musu00e9es, la visite des charmants villages de l’intu00e9rieur (Valldemossa, Deiu00e0), la du00e9couverte de la gastronomie locale dans les marchu00e9s et restaurants authentiques, et la participation aux fu00eates traditionnelles. C’est aussi un excellent moment pour profiter de l’observation des amandiers en fleur fin janvier.”}},{“@type”:”Question”,”name”:”Les restaurants et magasins sont-ils ouverts en janvier u00e0 Majorque ? ud83cudf7dufe0f”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Oui, la plupart des restaurants, bars et magasins u00e0 Majorque restent ouverts en janvier, en particulier dans les villes et villages principaux comme Palma. De nombreux u00e9tablissements saisonniers sur la cu00f4te peuvent u00eatre fermu00e9s, mais l’offre reste tru00e8s riche et permet de du00e9couvrir une Majorque plus authentique, fru00e9quentu00e9e par les locaux. Les prix peuvent u00eatre plus avantageux.”}},{“@type”:”Question”,”name”:”Faut-il louer une voiture pour un su00e9jour en janvier ? ud83dude97″,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Oui, la location de voiture est fortement recommandu00e9e pour un su00e9jour u00e0 Majorque en janvier. Elle vous offrira une flexibilitu00e9 maximale pour explorer l’u00eele u00e0 votre rythme, visiter les villages intu00e9rieurs, accu00e9der aux points de du00e9part de randonnu00e9e et du00e9couvrir des criques isolu00e9es, le tout sans les contraintes de la circulation estivale et avec des prix de location plus attractifs.”}}]}