// Début du script JavaScript pour le Tableau Comparateur
// 1. Données du tableau (fournies par l’utilisateur, complétées pour l’exemple)
// Cette structure JSON contient toutes les informations nécessaires pour générer le tableau.
// Elle est facile à modifier pour mettre à jour les activités ou les conditions.
const baliData = {
“title”: “Activités à Bali en Novembre : Quel temps, quelle activité ?”,
“headers”: [“Météo”, “Activités Recommandées”, “Lieux”, “Conseils Utiles”],
“rows”: [
[“Soleil Éclatant”, “Randonnée dans les rizières, plage, surf, visite de temples”, “Jatiluwih, Canggu, Tanah Lot”, “Crème solaire, chapeau et eau indispensables. Idéal pour les activités de plein air.”],
[“Averses Éparses”, “Visite de cascades (plus impressionnantes), cours de cuisine balinaise, ateliers d’art et d’artisanat, musées, séances de yoga et massages relaxants”, “Ubud, Tegalalang (cascades), Denpasar (musées)”, “Prévoir un parapluie ou un imperméable léger. Les paysages sont plus verts et les rizières luxuriantes !”],
[“Pluie Forte”, “Activités intérieures : spas de luxe, shopping artisanal (marchés couverts), galeries d’art, dégustation de café Luwak, cours de danse balinaise”, “Seminyak, Kuta, Sanur, Ubud”, “Idéal pour se détendre, explorer la culture locale à l’abri et profiter des services de bien-être.”],
[“Temps Variable”, “Exploration des volcans au lever du soleil (si le temps le permet), excursions en bateau (si la mer est calme et surveillée), visite de plantations de café et de thé”, “Mont Batur, Îles Gili (excursions en mer), Régions de Kintamani”, “Vérifier la météo locale et les conditions maritimes avant de partir. Soyez flexible avec vos plans d’activités en extérieur.”]
]
};
// Références aux éléments DOM
const tableHead = document.querySelector(‘#baliComparisonTable thead tr’);
const tableBody = document.querySelector(‘#baliComparisonTable tbody’);
const searchInput = document.getElementById(‘searchInput’);
const filterButtons = document.querySelectorAll(‘.filter-btn’);
const noResultsMessage = document.getElementById(‘noResultsMessage’);
let currentSortColumn = null;
let currentSortDirection = ‘asc’; // ‘asc’ ou ‘desc’
let currentFilter = ‘Tout’; // Filtre initial “Tout” pour afficher toutes les données
/**
* Met à jour l’apparence des boutons de filtre pour indiquer lequel est actif.
*/
function updateFilterButtons() {
filterButtons.forEach(button => {
if (button.dataset.filter === currentFilter) {
// Style pour le bouton actif
button.classList.add(‘bg-indigo-500’, ‘text-white’);
button.classList.remove(‘bg-gray-200’, ‘text-gray-800’, ‘hover:bg-gray-300’);
} else {
// Style pour les boutons inactifs
button.classList.remove(‘bg-indigo-500’, ‘text-white’);
button.classList.add(‘bg-gray-200’, ‘text-gray-800’, ‘hover:bg-gray-300’);
}
});
}
/**
* Génère et affiche le tableau avec les données actuelles, en tenant compte des filtres et du tri.
*/
function renderTable() {
tableBody.innerHTML = ”; // Nettoyer le corps du tableau avant de le recréer
noResultsMessage.classList.add(‘hidden’); // Cacher le message “aucun résultat” par défaut
// Appliquer les filtres
let filteredRows = baliData.rows.filter(row => {
// Filtrer par le type de météo sélectionné (sauf si “Tout” est choisi)
if (currentFilter !== ‘Tout’ && row[0] !== currentFilter) {
return false;
}
// Filtrer par le terme de recherche textuelle (recherche insensible à la casse dans toutes les colonnes)
const searchTerm = searchInput.value.toLowerCase();
if (searchTerm === ”) return true; // Si pas de recherche, toutes les lignes passent le filtre
return row.some(cell => String(cell).toLowerCase().includes(searchTerm));
});
// Appliquer le tri si une colonne est sélectionnée
if (currentSortColumn !== null) {
const columnIndex = baliData.headers.indexOf(currentSortColumn);
if (columnIndex !== -1) { // S’assurer que la colonne existe
filteredRows.sort((a, b) => {
const valA = String(a[columnIndex]).toLowerCase();
const valB = String(b[columnIndex]).toLowerCase();
if (valA valB) return currentSortDirection === ‘asc’ ? 1 : -1;
return 0;
});
}
}
// Si aucun résultat après filtrage/tri, afficher un message
if (filteredRows.length === 0) {
noResultsMessage.classList.remove(‘hidden’);
return;
}
// Créer et ajouter les lignes du tableau pour les données filtrées et triées
filteredRows.forEach(rowData => {
const tr = document.createElement(‘tr’);
tr.classList.add(‘hover:bg-indigo-50’, ‘transition-colors’, ‘duration-150’); // Effet visuel au survol
rowData.forEach((cellData, index) => {
const td = document.createElement(‘td’);
td.textContent = cellData;
td.classList.add(‘p-4’, ‘whitespace-normal’, ‘text-sm’, ‘text-gray-700’, ‘border-b’, ‘border-gray-100’);
// Mettre en gras la colonne “Météo” pour une meilleure lisibilité visuelle
if (index === 0) {
td.classList.add(‘font-semibold’, ‘text-indigo-700’);
}
tr.appendChild(td);
});
tableBody.appendChild(tr);
});
}
/**
* Génère les en-têtes du tableau et ajoute les écouteurs d’événements pour le tri.
*/
function renderTableHeaders() {
tableHead.innerHTML = ”; // Nettoyer les en-têtes existants
baliData.headers.forEach(headerText => {
const th = document.createElement(‘th’);
th.textContent = headerText;
th.classList.add(
‘py-3’, ‘px-4’, ‘text-left’, ‘text-xs’, ‘font-medium’, ‘uppercase’, ‘tracking-wider’,
‘cursor-pointer’, ‘select-none’, ‘relative’, ‘group’
);
th.style.minWidth = ‘120px’; // Assurer une largeur minimale pour chaque colonne
// Ajout d’une icône de tri visuelle (flèche)
const sortIcon = document.createElement(‘span’);
sortIcon.classList.add(‘ml-2’, ‘opacity-0’, ‘group-hover:opacity-100’, ‘transition-opacity’, ‘duration-200’, ‘absolute’, ‘right-2’, ‘top-1/2’, ‘-translate-y-1/2’);
sortIcon.innerHTML = ‘▼’; // Flèche vers le bas par défaut
th.appendChild(sortIcon);
// Gestion du tri au clic sur l’en-tête
th.addEventListener(‘click’, () => {
// Changer la direction de tri si la même colonne est cliquée à nouveau
if (currentSortColumn === headerText) {
currentSortDirection = currentSortDirection === ‘asc’ ? ‘desc’ : ‘asc’;
} else {
// Réinitialiser la direction si une nouvelle colonne est choisie
currentSortColumn = headerText;
currentSortDirection = ‘asc’;
}
// Mettre à jour toutes les icônes de tri pour refléter l’état actuel
document.querySelectorAll(‘#baliComparisonTable thead th span’).forEach(icon => {
icon.innerHTML = ‘▼’; // Réinitialiser toutes les icônes à la flèche par défaut
icon.classList.remove(‘rotate-180’, ‘opacity-100’); // Supprimer la rotation et l’opacité forcée
icon.classList.add(‘opacity-0’, ‘group-hover:opacity-100’); // Remettre l’opacité par défaut pour le hover
});
// Mettre à jour l’icône de la colonne actuellement triée
const currentSortIcon = th.querySelector(‘span’);
currentSortIcon.classList.remove(‘opacity-0’, ‘group-hover:opacity-100’);
currentSortIcon.classList.add(‘opacity-100’); // Rendre l’icône visible en permanence pour la colonne triée
if (currentSortDirection === ‘desc’) {
currentSortIcon.classList.add(‘rotate-180’); // Faire pivoter pour le tri descendant
}
renderTable(); // Re-render le tableau avec le nouveau tri
});
tableHead.appendChild(th);
});
}
// Initialisation : générer les en-têtes et le tableau pour la première fois
renderTableHeaders();
renderTable();
updateFilterButtons(); // S’assurer que le bouton “Tout” est initialement actif
// Ajout des écouteurs d’événements pour l’interactivité
searchInput.addEventListener(‘input’, renderTable); // Mettre à jour le tableau à chaque frappe dans le champ de recherche
filterButtons.forEach(button => {
button.addEventListener(‘click’, () => {
currentFilter = button.dataset.filter; // Mettre à jour le filtre actif
updateFilterButtons(); // Mettre à jour l’apparence des boutons
renderTable(); // Re-render le tableau avec le nouveau filtre
});
});
/*
* Section pour l’utilisation d’APIs externes (si nécessaire pour une fonctionnalité future)
*
* Conformément aux contraintes, l’outil actuel (le tableau comparatif) n’a PAS BESOIN
* de données externes pour fonctionner, car les données sont fournies directement
* dans le script JavaScript (variable `baliData`).
*
* Cependant, si une fonctionnalité additionnelle (hors du tableau principal)
* nécessitait une API gratuite, voici un exemple COMMENT on pourrait l’intégrer.
*
* Exemple : Récupérer et afficher l’heure actuelle à Bali.
* (Ce n’est pas intégré directement dans le tableau, mais sert d’illustration
* pour l’utilisation d’une API publique gratuite.)
*
* API choisie : World Time API (https://worldtimeapi.org/) – Gratuite, sans clé API, simple.
* URL de l’API : https://worldtimeapi.org/api/timezone/Asia/Jakarta
* (Jakarta est le fuseau horaire le plus proche de Bali pour des raisons de simplicité d’API).
*
* Exemple de réponse JSON typique de cette API :
* {
* “abbreviation”: “WITA”,
* “client_ip”: “1.2.3.4”,
* “datetime”: “2023-11-20T10:30:45.123456+08:00”,
* “day_of_week”: 1,
* “day_of_year”: 324,
* “dst”: false,
* “dst_from”: null,
* “dst_offset”: 0,
* “dst_until”: null,
* “raw_offset”: 28800,
* “timezone”: “Asia/Jakarta”,
* “unixtime”: 1700447445,
* “utc_datetime”: “2023-11-20T02:30:45.123456+00:00”,
* “utc_offset”: “+08:00”,
* “week_number”: 47
* }
*/
/*
// Fonction d’exemple pour récupérer l’heure actuelle à Bali via une API gratuite
// (Décommenter ce bloc et l’appel de fonction ci-dessous pour l’activer)
async function fetchBaliCurrentTime() {
const timeApiUrl = ‘https://worldtimeapi.org/api/timezone/Asia/Jakarta’;
try {
const response = await fetch(timeApiUrl);
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status} – Impossible de récupérer l’heure de Bali.`);
}
const data = await response.json();
const baliDateTime = new Date(data.datetime);
const formattedTime = baliDateTime.toLocaleTimeString(‘fr-FR’, {
hour: ‘2-digit’,
minute: ‘2-digit’,
timeZone: ‘Asia/Jakarta’
});
// Pour afficher cette information dans votre page, vous auriez besoin d’ajouter
// un élément HTML (ex:
) et de le mettre à jour ici :
// const timeDisplayElement = document.getElementById(‘baliTimeDisplay’);
// if (timeDisplayElement) {
// timeDisplayElement.textContent = `Heure actuelle à Bali : ${formattedTime} (WITA)`;
// } else {
// console.log(`Heure actuelle à Bali : ${formattedTime} (WITA)`);
// }
} catch (error) {
console.error(“Erreur lors de la récupération de l’heure de Bali:”, error);
// Gérer l’erreur (ex: afficher un message à l’utilisateur)
}
}
// Appel de la fonction pour récupérer et afficher l’heure de Bali (décommenter si désiré) :
// fetchBaliCurrentTime();
*/
// Fin du script JavaScript
{“@context”:”https://schema.org”,”@type”:”FAQPage”,”mainEntity”:[{“@type”:”Question”,”name”:”Est-ce que voyager u00e0 Bali en novembre est une bonne idu00e9e ? ud83cudf27ufe0f”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Oui, absolument ! Novembre marque le du00e9but de la saison humide, ce qui signifie des paysages d’une verdure luxuriante et des cascades impressionnantes. Les foules sont moins nombreuses, ce qui permet une expu00e9rience plus authentique et des prix potentiellement plus avantageux. Il faut juste anticiper des averses tropicales courtes mais intenses.”}},{“@type”:”Question”,”name”:”Quels sont les u00e9vu00e9nements culturels majeurs u00e0 Bali en novembre ? ud83dude4f”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Novembre est souvent la pu00e9riode des festivals de Galungan et Kuningan, des cu00e9lu00e9brations majeures de la victoire du bien sur le mal. Les rues sont du00e9coru00e9es de penjor, et de nombreuses cu00e9ru00e9monies ont lieu dans les temples, offrant une immersion culturelle profonde.”}},{“@type”:”Question”,”name”:”Quels types de vu00eatements doit-on emporter pour Bali en novembre ? ud83cudf92″,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Privilu00e9giez des vu00eatements lu00e9gers, respirants et u00e0 su00e9chage rapide en raison de l’humiditu00e9. Un impermu00e9able lu00e9ger ou un poncho, ainsi qu’un parapluie, sont indispensables. N’oubliez pas un bon ru00e9pulsif anti-moustiques et des chaussures confortables ru00e9sistantes u00e0 l’eau pour les explorations.”}},{“@type”:”Question”,”name”:”Peut-on faire de la plage et des activitu00e9s nautiques u00e0 Bali en novembre ? ud83cudfc4u200du2640ufe0f”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Oui, tout u00e0 fait ! Bien que des averses puissent survenir, les pu00e9riodes ensoleillu00e9es sont nombreuses et les plages restent accessibles. Le surf est souvent possible, et les eaux sont chaudes. Adaptez simplement vos activitu00e9s en fonction de la mu00e9tu00e9o du moment.”}},{“@type”:”Question”,”name”:”Comment se du00e9placer efficacement u00e0 Bali en novembre ? ud83dudef5″,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Les applications de transport comme Grab ou Gojek sont tru00e8s pratiques pour les taxis et les scooters avec chauffeur. La location de scooter est une option pour les conducteurs expu00e9rimentu00e9s, tandis que louer une voiture avec chauffeur est idu00e9al pour les excursions et les journu00e9es pluvieuses, offrant flexibilitu00e9 et confort.”}}]}