Vanilla web en pratique : créer une interface météo sans framework
Dans l’article précédent, « Vanilla web en 2026 : comprendre la plateforme avant d’empiler les frameworks », nous avons replacé le vanilla web dans le paysage actuel du développement : ce qu’il permet encore de faire directement dans le navigateur, pourquoi il reste utile de comprendre les standards natifs, et à quel moment un framework devient pertinent. Nous passons maintenant à une mise en pratique simple, en construisant une petite interface météo avec HTML, CSS et JavaScript natif.
L’objectif n’est pas de créer une application météo complète, ni de documenter Open-Meteo en détail. Cette API nous servira surtout de support pour suivre le parcours d’une donnée : une ville saisie dans un formulaire, un événement JavaScript, une requête HTTP, une réponse JSON, puis une mise à jour du DOM. À travers cet exemple volontairement limité, nous allons observer ce que le navigateur sait déjà faire, sans framework, sans bibliothèque externe et sans outil de build.
Une API météo pour suivre le trajet d’une donnée
Pour cet exercice, nous allons utiliser Open-Meteo, un service qui permet d’obtenir des informations météo à partir d’une requête HTTP. Nous n’allons pas explorer toute sa documentation, ni chercher à couvrir tous les paramètres disponibles. Ce qui nous intéresse ici est plus simple : construire une URL, envoyer une requête depuis le navigateur, recevoir une réponse au format JSON, puis afficher une partie de cette réponse dans notre page.

Ce choix nous donne un cas concret sans nous obliger à préparer un serveur, une base de données ou une clé d’accès. La page peut interroger directement l’API depuis JavaScript, ce qui rend le parcours plus facile à suivre. Pour une approche plus complète des API, de leur conception et de leurs bonnes pratiques, nous pouvons renvoyer aux articles consacrés aux API avec entre autres « Comprendre le concept de REST pour le développement d’applications web efficaces », « Mise en place d’une API RESTful CRUD : Côté Client », « Mise en place d’une API RESTful CRUD : Côté Serveur », ainsi qu’à celui portant sur « Optimisation des APIs RESTful : URLs & Bonnes pratiques ».
Dans notre cas, la météo impose tout de même une petite étape intermédiaire. Une ville saisie dans un formulaire ne suffit pas toujours à demander une météo fiable. Nous devons d’abord retrouver une position géographique, avec une latitude et une longitude. Ensuite seulement, nous pouvons demander les données météo correspondant à ce point. Ce détour est intéressant, parce qu’il rend visible le sens des échanges : une première requête transforme une saisie humaine en coordonnées exploitables, puis une seconde requête transforme ces coordonnées en données affichables.
Un même trajet, quels que soient les services
La même logique pourrait être observée avec d’autres services ouverts:
- Open Library permet par exemple de rechercher des livres et des auteurs à partir de ses API.
- Wikidata expose des données structurées interrogeables, notamment via son service SPARQL.
- Navitia donne accès à des données de transport public.
- Ou encore la Base Adresse Nationale qui propose une API pour rechercher ou géocoder des adresses françaises.
Les domaines changent, mais le trajet reste comparable : une interface prépare une demande, le navigateur l’envoie, le serveur répond, puis JavaScript lit cette réponse pour mettre à jour la page. Open-Meteo n’est donc pas la finalité de l’article. C’est simplement un support clair pour observer le vanilla web en action.

Ce que nous allons construire, simplement
Nous allons construire une petite interface météo volontairement limitée. Elle tiendra dans une seule page, avec un champ pour saisir le nom d’une ville, un bouton pour lancer la recherche, un message d’état et une zone de résultat. Cette zone restera masquée au départ, puis s’affichera uniquement lorsque des données météo auront été récupérées.

Le fonctionnement d’Open-Meteo nous oblige à distinguer deux moments. L’API météo ne part pas directement d’un nom de ville : elle attend une latitude et une longitude pour savoir quel point du globe interroger. Nous devons donc commencer par une première requête vers l’API de géocodage, qui transforme une saisie comme “Paris” en coordonnées géographiques (Latitude:48.856614,Longitude:2.3522219).
Une fois ces coordonnées obtenues, une seconde requête peut demander la météo actuelle pour ce lieu précis. La documentation d’Open-Meteo présente cette séparation entre l’API de géocodage, qui renvoie notamment latitude et longitude, et l’API météo, qui utilise ces coordonnées pour produire les données attendues.

On peut résumer cette logique avec un extrait volontairement simplifié. Le premier appel cherche une ville et récupère une position. Le second réutilise cette position pour demander la météo. Ce n’est pas encore le code complet de l’application, mais cela montre déjà le lien entre les deux requêtes.
// 1. Première requête : chercher les coordonnées d’une ville
const geocodingUrl = new URL('https://geocoding-api.open-meteo.com/v1/search');
geocodingUrl.searchParams.set('name', cityName);
geocodingUrl.searchParams.set('count', '1');
geocodingUrl.searchParams.set('language', 'fr');
geocodingUrl.searchParams.set('format', 'json');
const geocodingResponse = await fetch(geocodingUrl);
const geocodingData = await geocodingResponse.json();
// On récupère ici la première ville proposée par l’API.
const location = geocodingData.results[0];
// 2. Deuxième requête : demander la météo pour ces coordonnées
const weatherUrl = new URL('https://api.open-meteo.com/v1/forecast');
weatherUrl.searchParams.set('latitude', location.latitude);
weatherUrl.searchParams.set('longitude', location.longitude);
// Pour l’exemple, nous ne demandons qu’une donnée météo.
weatherUrl.searchParams.set('current', 'temperature_2m');
const weatherResponse = await fetch(weatherUrl);
const weatherData = await weatherResponse.json();Dans le code complet, nous ajouterons bien sûr des vérifications : champ vide, ville introuvable, réponse indisponible ou donnée manquante. Pour l’instant, ce fragment sert surtout à comprendre le passage essentiel : une première réponse fournit les coordonnées, puis ces coordonnées deviennent les paramètres de la seconde demande.
Un exemple limité pour observer la mécanique
Notre interface restera volontairement simple. Nous n’allons pas ajouter de carte, de prévisions sur plusieurs jours, de favoris, d’historique ou de compte utilisateur. Ces fonctions pourraient être utiles dans une vraie application météo, mais elles déplaceraient le sujet. Ici, nous voulons surtout voir comment une page construit une demande, interroge un service extérieur, lit une réponse et affiche quelques données utiles.

L’intérêt de cet exemple tient donc à sa mécanique. Une ville saisie dans un champ devient une recherche de coordonnées ; ces coordonnées servent ensuite à demander la météo ; la réponse JSON est lue par JavaScript ; puis le DOM est mis à jour pour rendre le résultat visible. C’est ce chemin, assez court mais complet, qui nous permet d’observer le vanilla web dans une situation concrète.
Trois fichiers pour garder les idées claires
Une fois le projet ouvert, trois fichiers suffisent pour comprendre le rôle de chaque partie de l’interface : HTML, CSS et JavaScript. Le premier pose les éléments visibles dans la page, le second règle leur présentation, le troisième ajoute le comportement. Cette séparation simple évite de tout mélanger au même endroit et rend le chemin de la donnée plus facile à suivre.
- Le fichier HTML pose le terrain. Il contient le titre, le formulaire, le champ de saisie, le bouton de recherche, le message d’état et les zones prévues pour afficher la météo. À ce stade, rien n’est encore dynamique. Nous préparons simplement les éléments que JavaScript pourra lire ou modifier plus tard. Le formulaire donne le point de départ de l’échange : une ville saisie par l’utilisateur.
- Le fichier JavaScript prend ensuite le relais. Il écoute l’envoi du formulaire, récupère la valeur saisie, construit les URLs nécessaires, interroge Open-Meteo avec
fetch(), lit les réponses JSON, puis injecte les données dans la page. C’est lui qui transforme une interface statique en interface active. Il ne remplace pas le HTML : il s’appuie sur lui pour savoir où lire et où afficher. - La feuille CSS intervient enfin pour rendre l’ensemble lisible et confortable. Elle organise la carte météo, le formulaire, les messages, la grille de données et les états visuels. Elle ne change pas la logique de l’application, mais elle aide à comprendre ce qui se passe : un message d’erreur doit être visible, un résultat doit être bien séparé du formulaire, un focus clavier doit rester identifiable. La présentation fait aussi partie de la clarté d’une interface.
Les sources complètes, et commentées, sont disponibles dans l’archive meteo-v1.zip, et une version de démonstration en ligne permet de tester cette première étape directement dans le navigateur. Nous garderons donc ici une lecture guidée du code, centrée sur la mécanique générale : préparer la demande, interroger le service, lire la réponse, puis mettre à jour la page. Le détail des paramètres Open-Meteo restera en arrière-plan, car l’enjeu reste principalement de comprendre le fonctionnement du vanilla web.
Ce que la première version rend visible
Tout cela se fait sans bibliothèque dédiée, sans composant prêt à l’emploi et sans framework. Le navigateur dispose déjà des éléments nécessaires : fetch() pour envoyer une requête, await pour attendre la réponse, .json() pour lire les données, puis les méthodes du DOM pour modifier la page. C’est précisément ce que cette première version permet de mettre en évidence : le vanilla web suffit à faire circuler une donnée complète, de la saisie jusqu’à l’affichage.

Le formulaire n’est pas seulement un bloc visuel. Il donne un point d’entrée clair à l’interaction : une ville est saisie, puis une action est déclenchée. En interceptant l’événement submit, JavaScript évite le rechargement de la page et garde l’utilisateur dans le même contexte. Ce petit geste résume déjà une grande partie des interfaces modernes : réagir à une action sans reconstruire toute la page.
form.addEventListener('submit', async function (event) {
event.preventDefault();
/*
Nous empêchons le rechargement de la page pour gérer la recherche
directement avec JavaScript : lecture de la saisie, préparation
de la requête et affichage d’un état d’attente.
*/
try {
/*
Si l’appel à l’API réussit, la réponse JSON est lue,
puis les données utiles sont affichées dans le DOM.
*/
} catch (error) {
/*
En cas d’erreur, l’interface garde un état clair :
un message explique ce qui s’est passé au lieu de laisser
la page sans indication.
*/
} finally {
/*
Succès ou erreur, certaines actions doivent toujours avoir lieu :
réactiver le bouton, arrêter l’état d’attente,
et éviter une interface bloquée.
*/
}
});La lecture du JSON montre aussi une distinction importante. L’API ne renvoie pas une page prête à afficher, mais des données structurées. JavaScript doit donc lire cette réponse, choisir les informations utiles, puis les placer dans les bons éléments du DOM. La température, l’humidité ou la vitesse du vent ne deviennent visibles qu’au moment où le script les relie à des éléments déjà présents dans le HTML.

Cet exemple montre enfin que la gestion des erreurs fait partie du parcours normal. Une ville peut être absente, mal saisie ou introuvable, la réponse peut être vide, le service peut être momentanément indisponible. Prévoir ces cas ne relève pas d’un supplément de confort. C’est ce qui évite à l’interface de rester dans un état ambigu, entre un bouton cliqué, un résultat absent et un utilisateur qui ne sait plus ce qui se passe.
Quand le nom d’une ville devient moins évident
Cette première version fonctionne tant que la ville saisie correspond assez bien à ce que l’API sait reconnaître. Pour une recherche simple comme Paris, Lyon, Londres, Bucarest ou encore Marseille, le parcours reste fluide : la ville est trouvée, les coordonnées sont récupérées, puis la météo peut être demandée. Mais dès que le nom devient plus composé, plus local, ou simplement saisi de manière différente, la recherche peut devenir moins fiable.
C’est souvent là que le problème apparaît. Par exemple, une ville comme Port-Saint-Louis-du-Rhône peut être connue sous sa forme administrative complète, avec ses traits d’union et son accent. Si nous saisissons “Port Saint Louis du Rhône”, “Port saint louis du rhone” ou “Port St Louis”, nous avons pourtant l’impression de désigner le même lieu. Pour une personne, l’intention reste claire. Pour l’API, la chaîne de caractères reçue peut ne plus correspondre à une entrée suffisamment précise.

Cette difficulté n’est pas propre à Open-Meteo, ni même à la météo. Elle apparaît dès qu’une interface reçoit une donnée formulée en langage humain, puis tente de la faire correspondre à une donnée structurée. Un nom de ville, une adresse, un auteur, une gare ou un arrêt de transport peuvent avoir plusieurs écritures possibles. Ce que nous tapons naturellement n’est pas toujours ce que le service attend techniquement.
Ce premier accroc est intéressant, parce qu’il ne remet pas en cause toute l’interface. La mécanique générale fonctionne : formulaire, requête, réponse JSON, mise à jour du DOM. Ce qui manque, c’est une aide au moment de la saisie. Plutôt que d’attendre l’erreur pour dire que la ville n’a pas été trouvée, nous pouvons accompagner la recherche plus tôt, en proposant des suggestions de lieux à partir des premiers caractères saisis.
Accompagner la saisie avant la recherche
Le problème rencontré ne vient pas de la météo elle-même, mais de la manière dont une ville est reconnue. Dans la première version, nous envoyons directement le texte saisi à l’API de géocodage. Si ce texte correspond à un nom connu, tout fonctionne. Mais si le nom est composé, abrégé ou écrit autrement, la recherche peut échouer. Nous devons donc éviter de faire reposer toute la suite du parcours sur une simple chaîne de caractères.
Comme pour la première version, les sources de cette seconde étape sont disponibles dans l’archive meteo-v2.zip, avec les commentaires nécessaires pour suivre le détail du code. La démo v2 en ligne permet également de comparer directement le comportement avec (v2) et sans aide à la saisie (v1).
Le contournement consiste à ajouter une étape avant la recherche météo. Au lieu d’attendre la validation du formulaire, nous observons la saisie pendant qu’elle se construit. Dès que le texte devient assez long, JavaScript demande à l’API de géocodage plusieurs lieux possibles. L’utilisateur peut alors choisir une proposition, et ce choix nous donne une donnée plus solide qu’un texte libre : un nom identifié, un pays, une latitude et une longitude.
cityInput.addEventListener('input', function () {
/*
La saisie change.
Avant de préparer des suggestions, nous vérifions que le texte
est assez long et nous évitons d’interroger l’API à chaque frappe.
*/
prepareCitySuggestions();
});
async function prepareCitySuggestions() {
/*
Cette fonction sert de carrefour :
- si la saisie est trop courte, elle ferme la liste ;
- sinon, elle demande plusieurs villes possibles ;
- puis elle transmet ces résultats à l’affichage.
*/
const suggestions = await findCitySuggestions(cityInput.value);
showCitySuggestions(suggestions);
}
async function findCitySuggestions(cityName) {
/*
Ici se trouve la requête vers le géocodage Open-Meteo.
Elle ne demande pas encore la météo :
elle cherche seulement des lieux correspondant au texte saisi.
*/
return results;
}
function showCitySuggestions(suggestions) {
/*
Les résultats reçus deviennent des suggestions cliquables.
L’utilisateur ne choisit donc plus seulement un texte,
mais une ville reconnue par l’API.
*/
}
function chooseCity(location) {
/*
La suggestion choisie est mémorisée sous forme d’objet complet :
nom, pays, latitude, longitude.
La recherche météo pourra ensuite repartir de cette donnée stabilisée.
*/
selectedLocation = location;
}Ce fragment ne cherche pas à montrer toute l’implémentation. Il sert surtout à lire le cheminement : une saisie déclenche une recherche de suggestions, les suggestions sont affichées, puis le choix d’une ville fournit une donnée plus fiable que le texte saisi au départ. Le code complet ajoute ensuite les détails nécessaires : délai avant requête, nettoyage de la liste, attributs d’accessibilité et affichage plus précis des lieux proposés.

Cette solution a malgré tout ses limites. Si nous saisissons port-saint-lou, l’API peut proposer Port-Saint-Louis-du-Rhône, même sans accent dans la suite du nom. En revanche, si nous écrivons port saint lou, sans les traits d’union, la suggestion peut ne pas apparaître. L’autocomplétion améliore donc le parcours, mais elle ne règle pas tous les écarts entre langage naturel et données structurées.

C’est un point important pour rester dans le cadre de l’article. Nous ne cherchons pas ici à finaliser une application météo parfaitement ergonomique, ni à couvrir tous les cas de saisie possibles. Nous montrons plutôt qu’avec les standards du navigateur, un événement input, un délai avant requête, une liste générée dans le DOM et une donnée sélectionnée, nous pouvons déjà construire une interface plus riche, sans framework et sans bibliothèque dédiée.
Ce que l’autocomplétion change dans notre manière de penser
Avec cette seconde version, nous ne changeons pas seulement un détail d’interface. Nous déplaçons une partie du travail en amont de la recherche météo. Au lieu d’attendre qu’une ville soit introuvable pour afficher une erreur, nous aidons la saisie à se rapprocher progressivement d’un lieu reconnu par l’API. L’interface devient alors un espace de préparation, pas seulement un point d’envoi.
Ce déplacement est important, parce qu’il montre une limite fréquente des échanges avec un service distant. Une API attend des données structurées, alors que nous saisissons souvent des mots approximatifs, incomplets ou écrits selon nos habitudes. L’autocomplétion ne règle pas tous les cas, mais elle réduit l’écart entre ces deux mondes : la formulation humaine d’un côté, la donnée exploitable de l’autre.
Là encore, aucun framework n’est nécessaire pour comprendre le principe. Un événement input, un délai avant requête, une liste de suggestions, un choix mémorisé, puis une recherche météo plus stable : le navigateur fournit déjà les briques nécessaires. La valeur de l’exemple n’est pas de produire une application parfaite, mais de rendre visible une manière de raisonner : mieux préparer la donnée avant de l’envoyer.
Conclusion : Revenir à la plateforme
Le vanilla web n’est pas une manière de refuser les outils modernes. Il nous rappelle simplement que le navigateur est déjà une plateforme complète, avec ses langages natifs, son moteur d’exécution JavaScript et ses API intégrées, comme le DOM, les événements ou fetch(). Ces briques ne remplacent pas les services externes que nous interrogeons, comme Open-Meteo, mais elles nous donnent les moyens de dialoguer avec eux directement depuis la page.
C’est pour cela qu’il reste utile de pratiquer ces mécanismes sans surcouche immédiate. Lorsque nous écrivons un formulaire, que nous écoutons un événement, que nous appelons un service distant avec fetch() ou que nous mettons à jour le DOM, nous ne faisons pas un exercice “sans framework” par principe. Nous observons le fonctionnement réel du web, dans sa forme la plus lisible. Les outils pourront venir ensuite, mais ils ne devraient pas nous empêcher de comprendre ce qu’ils organisent à notre place.
Cette approche donne aussi une autre relation au code. Elle oblige à nommer les étapes, à distinguer les responsabilités, à voir ce qui relève de la structure, du comportement, de la présentation ou de la donnée. Elle rend les choix plus visibles. Et lorsqu’un framework devient nécessaire, nous l’abordons alors autrement : non comme une solution automatique, mais comme une couche supplémentaire posée sur un terrain que nous connaissons déjà.
Revenir au vanilla web, ce n’est donc pas revenir en arrière. C’est reprendre contact avec la plateforme, avec ses possibilités immédiates et ses limites concrètes. C’est aussi garder une forme d’autonomie : savoir construire une interface simple, comprendre le trajet d’une donnée, puis décider ensuite ce qui mérite vraiment d’être abstrait, automatisé ou confié à un outil plus large.
