µJS est une bibliothèque JavaScript open source (licence MIT) qui permet de rendre un site web dynamique sans recourir à un framework frontend lourd. Elle s’inspire de pjax, Turbo et HTMX, avec pour objectif d’être plus simple et plus légère.
Principe de fonctionnement
µJS intercepte les clics sur les liens et les soumissions de formulaires pour charger les pages via AJAX, au lieu de déclencher un rechargement complet du navigateur. Le contenu récupéré remplace tout ou partie de la page courante. Le résultat : une navigation fluide, sans rechargement visible, sans écrire une seule ligne de JavaScript.
Aucune étape de build, aucune dépendance, compatible avec n’importe quel backend (PHP, Python, Go, Ruby…).
Fonctionnalités principales
- Mode patch : mettre à jour plusieurs fragments du DOM en une seule requête, via des attributs
mu-patch-targetdans la réponse HTML du serveur - SSE : mises à jour en temps réel via Server-Sent Events
- DOM morphing : préservation de l’état du DOM (focus, scroll, transitions CSS) via idiomorph
- View Transitions : animations fluides entre les états de page, via l’API native du navigateur
- Prefetch : préchargement de la page cible au survol d’un lien
- Polling : rafraîchissement automatique d’un fragment à intervalle régulier
- Verbes HTTP complets : GET, POST, PUT, PATCH, DELETE sur n’importe quel élément
- Barre de progression : intégrée, sans dépendance externe
Installation
Via CDN :
<script src="https://cdn.jsdelivr.net/npm/@digicreon/mujs/dist/mu.min.js"></script>
<script>mu.init();</script>
Via npm :
npm install @digicreon/mujs
Exemple 1 : navigation AJAX sans configuration
Par défaut, tous les liens internes sont interceptés automatiquement. Le <body> de la page cible remplace le <body> courant.
<!DOCTYPE html>
<html>
<head>
<title>Mon site</title>
</head>
<body>
<nav>
<a href="/">Accueil</a>
<a href="/articles">Articles</a>
<a href="/contact">Contact</a>
</nav>
<main id="contenu">
<p>Contenu de la page.</p>
</main>
<script src="https://cdn.jsdelivr.net/npm/@digicreon/mujs/dist/mu.min.js"></script>
<script>mu.init();</script>
</body>
</html>
Aucun attribut supplémentaire. Les boutons retour/avant du navigateur fonctionnent, l’URL est mise à jour, le titre de la page aussi.
Pour ne remplacer qu’un fragment de la page plutôt que le <body> entier :
<a href="/articles" mu-target="#contenu" mu-source="#contenu">Articles</a>
Dans ce cas, µJS va récupérer la page /articles, va extraire l’élément #contenu de la réponse, et remplace l’élément #contenu courant avec.
Si tous les changements de pages se font dans l’élément #contenu, on peut généraliser dans la configuration (pour éviter d’avoir à mettre des attributs mu-target et mu-source sur tous les liens) :
<script>
mu.init({
target: "#contenu",
source: "#contenu"
});
</script>
Exemple 2 : recherche en direct avec debounce
<input type="text" name="q"
mu-trigger="change"
mu-debounce="300"
mu-url="/recherche"
mu-target="#resultats"
mu-source="#resultats"
mu-mode="update">
<div id="resultats"></div>
Le serveur reçoit une requête GET vers /recherche?q=... et retourne un fragment HTML. µJS l'injecte dans #resultats. Aucun JavaScript à écrire côté client.
Exemple 3 : mise à jour de plusieurs fragments en une seule requête (patch mode)
Côté HTML :
<form action="/commentaire/ajouter" method="post" mu-mode="patch">
<textarea name="contenu"></textarea>
<button type="submit">Envoyer</button>
</form>
<ul id="commentaires">
<!-- liste des commentaires -->
</ul>
<span id="compteur">3 commentaires</span>
Le serveur retourne plusieurs fragments HTML dans une seule réponse. Chaque fragment indique sa cible via mu-patch-target :
<!-- Ajoute le nouveau commentaire à la liste -->
<li class="commentaire" mu-patch-target="#commentaires" mu-patch-mode="append">
<p>Le nouveau commentaire</p>
</li>
<!-- Met à jour le compteur -->
<span mu-patch-target="#compteur">4 commentaires</span>
<!-- Réinitialise le formulaire -->
<form action="/commentaire/ajouter" method="post" mu-patch-target="form">
<textarea name="contenu"></textarea>
<button type="submit">Envoyer</button>
</form>
Une seule requête HTTP, trois fragments mis à jour simultanément. Le serveur garde le contrôle total sur ce qui est mis à jour et comment.
Aller plus loin
- Site officiel (315 clics)
- Playground interactif (141 clics)
- Dépôt GitHub (111 clics)
- Article de blog (76 clics)

# Ça va casser le partage de lien...
Posté par Tonton Th (site web personnel, Mastodon) . Évalué à 3 (+2/-1).
J'ai un peu joué avec la démo, c'est assez convaincant, mais ça crée un gros problème : l'URL ne change plus, et donc il est impossible de l'envoyer à quelqu'un :
[^] # Re: Ça va casser le partage de lien...
Posté par Amaury Bouchard (site web personnel) . Évalué à 4 (+3/-0).
Non, ça dépend. Par défaut, l'historique de navigation est mis à jour au fur et à mesure des chargements de pages.
Il est possible de désactiver le changement d'historique (soit individuellement sur le lien, soit par configuration globale). Sur la page de Playground, je l'ai justement désactivé pour éviter qu'un rafraîchissement intempestif fasse sortir de la démo ; on n'est pas dans le cadre d'une navigation classique.
Mais si tu regardes le reste du site (qui utilise évidemment µJS), en naviguant de la page d'accueil vers la documentation, puis vers la page "About", tu verras que l'URL change. Tu peux recharger la page, tu peux copier-coller l'URL, etc.
Le principe de base est vraiment que ce soit transparent, que le comportement soit exactement le même que la navigation habituelle, mais juste plus fluide et plus rapide.
[^] # Re: Ça va casser le partage de lien...
Posté par Yves (site web personnel) . Évalué à 2 (+1/-0).
Je pense que Tonton Th parlait de clic-droit sur le lien > copier le lien, plutôt que de copier l’URL de la page courante une fois qu’on y est déjà.
Bref, a priori, le risque est de fournir à quelqu’un une URL qui ne fournit que des fragments.
[^] # Re: Ça va casser le partage de lien...
Posté par bosskev . Évalué à 1 (+1/-0).
Sur firefox 140esr ici, l'URL change bien
# htmx
Posté par wilk . Évalué à 2 (+0/-0).
Par rapport à htmx si je comprends bien on a un fonctionnement par défaut et plus général alors qu'avec htmx il faut être plus explicite et donc plutôt adapté à une utilisation avec parcimonie ?
[^] # Re: htmx
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+0/-0).
Il y a de ça, oui. La philosophie de µJS se rapproche plus de Turbo : offrir un fonctionnement le plus transparent possible pour rendre la navigation fluide et rapide.
Ensuite, tu peux faire des choses plus spécifiques sur chaque liens, en se rapprochant de ce que propose htmx. Par exemple, le mode patch qui permet de mettre à jour plusieurs portions d'une page en une seule requête, qui s'utilise de la même manière en AJAX classique et en SSE, et qui est un modèle plus simple que le
hx-swap-oobde htmx.# Utilisation d'IA - Claude
Posté par HS-157 . Évalué à 5 (+9/-4).
Je suis tombé sur ton projet il y a quelques jours et j'ai vu qui il y a une utilisation d'un fichier
CLAUDE.mddans le dépôt ainsi que dans tes autres projets.Dommage de pas le dire dans le journal et malheureusement, à cause de ça, ça donne pas envie de l'utiliser.
[^] # Re: Utilisation d'IA - Claude
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+4/-4).
J'utilise Claude pour générer la documentation et faire de la revue de code.
Il va falloir s'habituer à voir l'IA être utilisée dans les projets libres (cf. la RFC de LLVM intitulée "LLVM AI tool policy: human in the loop"). Tant que c'est fait intelligemment, je n'y vois personnellement pas de problème. On peut avoir un point de vue différent, mais en l'occurrence je ne vois pas quel serait le gain pour qui que ce soit que la documentation soit de moins bonne qualité (sur le fond comme sur la forme) par manque de temps de ma part.
[^] # Re: Utilisation d'IA - Claude
Posté par wilk . Évalué à 4 (+6/-4).
Ha ben on est pas prêt de s'y habituer alors !
[^] # Re: Utilisation d'IA - Claude
Posté par steph1978 . Évalué à 5 (+8/-5).
Personnellement je ne pense pas que ce champ commentaire soit suffisamment grand pour que je puisse écrire tout ce que j'ai à en redire.
Je passe les aspects étiques et écologiques qui sont pour moi déjà des bloqueurs.
En restant simplement sur le plan de la contribution à un projet open source. Je pense que les LLM, mêmes les meilleurs, ne sont pas à niveau. Ils ne savent pas prendre les bonnes abstractions, et encore moins les maintenir et les amender dans le temps. Ils hallucinent des patterns ou des bibliothèques qui n'existent pas. Ils produisent dix fois trop de code pour le même résultat.
Je n'utilise pas d'agent pour coder. Si je dois contribuer à un projet, je n'ai pas envie de comprendre dans la tartine de code qu'il produit, ce que le LLM a halluciné, ce qu'il a essayé de construire, ce qui fonctionne ou ne fonctionne pas. Je ne dis pas que tous les humains font mieux mais la plupart font mieux et à défaut, ils font moins.
Et puis pour moi, open source, c'est pouvoir disposer des éléments qui me permettent de recréer le logiciel. Quand le code est généré, ce n'est pas la source. La source, c'est le prompt et le 670 milliards de paramètres du modèle.
De mon point de vue, soit tu choisis de développer avec un agent soit avec des humains (soit seul mais là ça clos le débat); mais je ne vois pas comment les deux peuvent être compatibles.
Quant à la pérennité, bonne chance quand le LLM ne comprendra même plus ce qu'il a créé, quand la boite qui l'opère aura fermé boutique ou quand ils appliqueront les prix réels, € x100. On constate aussi au fil des articles à quel point la qualité des logiciels plonge quand leur équipes des dev ont recours au agent de codage (cf Amazon, Github, M$).
La technologie est belle. Elle peut avoir des usages. Mais actuellement elle est en train de tuer l'open source par les deux bouts : d'une part en déversant des une quantité ingérable de contributions daubées, d'autre part en rendant la nécessité
d'écrirede maintenir des logiciels inutile puisqu'ils peuvent être recrée d'une simple phrase ; utopie à date mais qui fait son chemin dans les esprits.Après, peut être que la technologie va s'améliorer au point de ne pas détruire la planète, de produire du code de qualité. Peut être, plus sûrement, que, comme youtube et le streaming, on va assister à une déferlante de médiocrité mais aussi à l'émergence de quelques pépites qui n'auraient pas été possibles sans. Qui sait.
Je ne dis pas que ce projet en particulier est dans ce cas, je n'ai pas audité le code, et la doc a l'air clean. Mais c'est vrai que quand je vois que ça peut avoir été codé par/avec un agent, j'ai tendance à ne pas y mettre les doigts.
[^] # Re: Utilisation d'IA - Claude
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+0/-0).
Je n'ai pas grand-chose à répondre à tout ça. Surtout que je suis d'accord avec une partie de ce que tu as écrit.
Mais il n'empêche qu'il y a déjà plus de projets open-source qui utilisent l'IA qu'on ne peut le croire (cf. l'exemple de LLVM que j'ai cité, mais j'en connais d'autres). Et dans des mesures que peu de gens soupçonnent.
[^] # Re: Utilisation d'IA - Claude
Posté par BAud (site web personnel) . Évalué à 1 (+1/-2).
Mais il n'empêche qu'il y a déjà plus de projets open-source qui n'utilisent pas l'IA qu'on ne peut le croire (cf. l'exemple de [insérer projet sans IA ici], mais j'en connais d'autres). Et dans des mesures que peu de gens soupçonnent.
les deux sont tout aussi vrais, ainsi formulés ;-)
[^] # Re: Utilisation d'IA - Claude
Posté par barmic 🦦 . Évalué à 2 (+1/-1). Dernière modification le 12 mars 2026 à 15:16.
La question n’est pas de savoir ce qui est vrai ou non, mais ce que ça apporte au sujet : les IA peuvent elles aider à coder ?
Ton affirmation est à côté de la plaque et servirait à répondre à : est-il possible de coder sans IA ?
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: Utilisation d'IA - Claude
Posté par LaurentClaessens (site web personnel) . Évalué à 5 (+3/-0).
Il y a un monde entre
«Code moi un projet complet qui fait ça et ça»
et
«
Écris une fonction qui prend en entrée le chemin d'un fichier image, l'ouvre, inverse les canaux bleu et vert, puis le sauve en ajoutant "_BG" dans le nom de fichier.
»
Certes dans le deuxième cas on a le risque que l'IA invente des méthodes OpenCV qui n'existent pas (divulgâchis: ce n'est pas le cas), mais en gros, en lisant le code on comprend et c'est facilement testable.
Et puis surtout : si je sais que j'ai besoin d'une fonction qui fait exactement ça, c'est que je sais comment fonctionne le projet.
ai_usage.md
En fait ce serait une bonne pratique d'ajouter un fichier
ai_usage.mddans nos projets pour expliquer quel usage de l'IA est fait dans le projet.Les écrivains et les musiciens devraient également prendre la bonne habitude d'écrire un tel texte pour expliquer quelle est la part d'IA et quelle est la part d'invention humaine[1].
Allez go. J'écris le mien.
[1] Je soupçonne fortement celui-ci d'avoir bossé nettement plus que «écris une musique sympa avec des cuivres et des images steampunk».
[^] # Re: Utilisation d'IA - Claude
Posté par BAud (site web personnel) . Évalué à 3 (+1/-0). Dernière modification le 14 mars 2026 à 12:43.
hmmm pour ton 1er exemple sur la limite, il y a
, ça se lit à peu près…
https://www.ilemaths.net/sujet-difference-entre-limite-classique-et-limite-epointee-763859.html qui parle de limite épointée et, malgré toutes les fautes en
forcément, si tu prends une fonction non continue en 0 — définie par
et
— il vaut mieux connaître la différence entre les deux définitions possibles de limite… Tu attendais de l'IA une précision par rapport à Bourbaki ? Autant l'indiquer, sinon je ne comprend que difficilement ta remarque des résultats inexacts de l'IA : elle ne prend pas la bonne définition, bin vala tu l'as collée.
HS : en fait, j'aime bien ton écriture mathématiquépicène (et les mots-valises qui se prononcent), faudrait proposer de l'ajouter en 7. à https://fr.wikipedia.org/wiki/Wikip%C3%A9dia:%C3%89criture_%C3%A9pic%C3%A8ne (vu que les 5. et 6. ont tendance à m'horripiler :/)
[^] # Re: Utilisation d'IA - Claude
Posté par LaurentClaessens (site web personnel) . Évalué à 3 (+1/-0).
Je vais tenter de ne pas troller sur ce sujet maintenant, mais ça me brûle les doigts.
Ce que je voulais dire est qu'un étudiant français (qui a la limite pointée dans son cours) risque de se faire avoir si il demande des choses à l'IA parce que la limite pointée est complètement inconnue en-dehors du microcosme de l'enseignement en France. Tellement qu'elle est inconnue de l'IA.
La différence entre la limite pointée et épointée fait partie de ces choses que les étudiants ignorent d'ignorer (contrairement à leurs formules de trigono qu'ils savent ignorer).
Et le pire est que l'étudiant n'aurait aucun moyen de se rendre compte que l'IA lui dit des fautes (par rapport à la définition de son cours) parce que le plus souvent le prof ne prévient pas ses étudiant que la définition
donnée en France est TRÈS minoritaire par rapport à la définition
.
Mon point était surtout de dire que, si on veut utiliser de l'IA pour faire des math, il faut vraiment avoir le niveau parce que l'IA peut poser des pièges inattendus.
Bref. Je vais réviser ma copie parce que je n'ai manifestement pas été clair. Merci.
Moi, je ne vais pas le faire parce que ce serait assez prétentieux de ma part. Par contre si quelqu'un d'autre le faisait, j'en mourrais d’orgueil et de vanité :)
Mais pour être honnête, je crois que je suis le seul à faire de l'écriture inclusive "en moyenne". Ça ne vaut pas mention sur Wikipédia :(
[^] # Re: Utilisation d'IA - Claude
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3 (+1/-0).
…en moyenne en français, tu es la première et seule plume que j’ai lu. Pour d’autres idiomes, je vois même proposés les paquetages he-she (2016, anglais, États-Unis d’Amérique) et gender (2015, allemand et possiblement français, Allemagne) :)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Utilisation d'IA - Claude
Posté par LaurentClaessens (site web personnel) . Évalué à 2 (+0/-0). Dernière modification le 25 mars 2026 à 22:25.
Le mien est mieux que he-she (même pas honte) parce que le mien n'est pas réellement aléatoire.
Ma macro regarde la parité de "page + numéro du dernier théorème". Grâce à ça je peux faire
Et je sais que les deux
\randomGendersélectionneront le même.Du coup ma macro n'est pas cryptographiquement sure.
Et maintenant que je le dis, je crois que ce serait mieux de faire "numéro du chapitre + numéro dernier théorème".
[^] # Re: Utilisation d'IA - Claude
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2 (+0/-0).
Maintenant que tu le dis, il faut extraire le code dans un fichier (qui deviendra une extension de plus) et prévoir de pouvoir paramétrer le compteur à utiliser… (si je fais un article sans théorème, que je puisse réutiliser en me basant sur les sous-sections…) :)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Utilisation d'IA - Claude
Posté par vpo . Évalué à 6 (+6/-1).
Il dit explicitement qu'il n'a pas codé avec une IA.
Et même si il utilisait l'IA pour coder, je ne comprends pas bien l'argument suivant :
Qu'est-ce que ça veut dire recréer le logiciel ? Tu as le code et tu as la doc qui va avec. Que ce soit généré par un humain ou par une machine importe peu. Avant qu'il y ait des IA, tout était fait à la main, et je ne pense pas que tu aurais exigé les brouillons et l'historique des recherches sur Stackoverflow de la personne qui a pondu les lignes de code. Si tu as le code source, la doc qui explique l'architecture et comment générer le binaire, tu as tout pour te plonger dans le code, proposer tes propres patchs et générer ton propre binaire.
Le seul cas où je serai d'accord avec toi c'est dans le cas où on aurait un LLM qui passerait du prompt au binaire sans passer par l'étape génération d'un code source.
[^] # Re: Utilisation d'IA - Claude
Posté par Yves (site web personnel) . Évalué à 1 (+1/-1).
Très caricatural, tout ça. Il y a d’autre manière d’utiliser l’IA que « Fais ça pendant que je vais à la plage. Merci. »
Ce qui importe, c’est que l’humain garde la maîtrise, et donc s’approprie et contrôle ce qui est proposé. Bien utilisée, l’IA devient un partenaire de travail, un peu comme en pair-programming.
[^] # Re: Utilisation d'IA - Claude
Posté par BAud (site web personnel) . Évalué à 2 (+1/-1).
en pair-proggramming tu as un ou une paire
avec une IA, bin, t'as une IA, même pas un brelan quoi !
[^] # Re: Utilisation d'IA - Claude
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3 (+1/-0).
J’ai cru lire dans un autre commentaire (sous un autre journal ou lien) ce matin que c’est plus un-e éternel-le stagiaire et non un-e pair :/
Avec deux ça n’en fera pas un carré non plus.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
# Problème avec Safari
Posté par Fernando . Évalué à 1 (+0/-0).
Pas mal, mais avec Safari (version 26.3.1 sur macOS, pas testé sur iPhone) et sur la partie documentation, la navigation est d'une lenteur abominable. Il doit y avoir un problème avec la fonction _onPopState() qui tombe en erreur :
RangeError: Maximum call stack size exceeded.
D'où la lenteur dés qu'on clique dans la barre de navigation.
[^] # Re: Problème avec Safari
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+0/-0).
Ah merci, je vais regarder ça. Je suis sous Linux, donc je n'ai pas pu tester sur Safari. J'avais aussi cru voir une lenteur, mais je n'ai pas pu la reproduire. Au moins là j'ai une piste ! :)
[^] # Re: Problème avec Safari
Posté par Adrien Dorsaz (site web personnel, Mastodon) . Évalué à 2 (+0/-0). Dernière modification le 11 mars 2026 à 17:37.
Sous Linux, tu pourrais essayer avec GNOME Web, il utilise le même moteur que Safari (Webkit).
[^] # Re: Problème avec Safari
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+0/-0).
Merci ! Je viens de l'installer. Et… malheureusement je ne reproduis toujours pas le ralentissement :/
C'est compliqué, mais il semblerait que certaines versions de Safari gèrent mal le déclenchement de popstate. Je vais continuer à creuser.
[^] # Re: Problème avec Safari
Posté par Fernando . Évalué à 1 (+0/-0).
Oui de mémoire il y a des histoires de sécurité ajoutées par Apple, mais je ne sais plus quoi, désolé.
[^] # Re: Problème avec Safari
Posté par Fernando . Évalué à 2 (+1/-0). Dernière modification le 11 mars 2026 à 19:02.
Et sinon c'est la ligne :
window.location.href = document.location
de la fonction _onPopState() quand le state de l'event est à null qui provoque la boucle infinie. Peut être que tu devrais comparer window.location.href à document.location.href et ne faire l'affection que s'il y a une différence. L'affectation doit probablement produire une event popstate d'office avec Safari et du coup cela tourne en rond.
[^] # Re: Problème avec Safari
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+0/-0).
Encore merci :)
Je vais regarder dans cette direction !
[^] # Re: Problème avec Safari
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+0/-0). Dernière modification le 13 mars 2026 à 14:56.
J'ai appliqué un correctif : j'ai remplacé le
window.location.href = document.locationparlocation.reload(). J'évite aussi de sauver l'état du scroll pendant le popstate.C'est tagué dans la version 1.4.5.
# contenus et habillage
Posté par steph1978 . Évalué à 5 (+3/-0).
J'ai une question de conception (et peut être de compréhension) qui doit probablement s'appliquer à µJS et aux autres bibliothèques.
Disons que j'ai deux contenus : http://example.com/contents/a et http://example.com/contents/b
Quand je charge A, j'ai bien envie d'avoir un lien vers B qui ne recharge pas la page mais qui remplace le cœur de la page avec le contenu B. Mais quand je charge directement B, j'ai besoin de toute la page.
Je crois comprendre que ces bibliothèques servent à ce cas d’usage … côté client.
Mais comment le back-end, quand il reçoit une requête pour un contenu sait qu'il doit renvoyer toute la page ou seulement le cœur de la page ?
[^] # Re: contenus et habillage
Posté par Amaury Bouchard (site web personnel) . Évalué à 3 (+2/-0).
Ce que tu décris est effectivement le cas de base, et il peut être traité de 2 manières différentes.
Solution basique
Tes deux pages "A" et "B" sont des pages normales, qui contiennent un HTML complet. Par défaut, en activant µJS, quand on clique sur un lien (par exemple, sur la page A, un lien qui pointe vers B), la lib va chercher le contenu de la page B en AJAX. La balise
<body>de la page B est extraite du contenu récupéré, et elle est utilisée pour remplacer la balise<body>de la page courante.Dans ce cas il n'y a rien à faire côté back-end. Tu génères tes pages comme d'habitude. µJS va juste rendre la navigation plus rapide et plus fluide, et évitant de refaire tout le traitement CSS et JS. L'écran ne "clignote" pas au chargement de la page.
Solution avancée
Si tu veux faire quelque chose de plus pointu, tu peux ne vouloir renvoyer qu'une portion de la page au lieu de la page complète. Dans ce cas, ton code backend doit regarder s'il y a un en-tête HTTP
X-Requested-With(contenant la valeurXMLHttpRequest), pour faire un traitement conditionnel.Mais honnêtement, la plupart du temps on peut s'en passer et rester sur le fonctionnement de base.
[^] # Re: contenus et habillage
Posté par Fernando . Évalué à 3 (+2/-0).
Je vois 2 headers spécifiques (la doc dit qu'il y en a d'autres) :
X-Mu-Prefetch: 1
X-Requested-With: mujs (donc pas XMLHttpRequest)
Du coup le backend peut savoir que c'est µJS qui a fait l'appel mais comment peut-il savoir quel fragment servir ? C'est d'office le tag body ?
Et quid du cache HTTP, c'est la même URL en GET du coup s'il y a du cache cela risque de cafouiller non ?
Par ailleurs que se passe t'il si on utilise un service worker (PWA) ? Ces headers vont-ils passer ?
[^] # Re: contenus et habillage
Posté par Amaury Bouchard (site web personnel) . Évalué à 2 (+1/-0).
Headers complets : il y en a 4 en tout.
X-Requested-With: XMLHttpRequest,X-Mu-Mode(mode d'injection courant),X-Mu-Method(pour les requêtes non-GET), etX-Mu-Prefetch: 1(sur les prefetch uniquement).Le
X-Requested-With: muJSétait sur une ancienne version de µJS (et notamment visible sur le site mujs.org, qui vient d'être mis à jour)Quel fragment servir : deux approches. Soit le serveur retourne la page complète et µJS extrait lui-même le bon fragment via
mu-source. Soit le serveur détecteX-Requested-Withet retourne uniquement le fragment. C'est le développeur qui choisit.Pour illustrer les choses, imaginons que tu aies un page qui affiche 10 items (sur une liste qui peut en contenir plus), ainsi que le nombre total d'items. Tu auras une page qui ressemble à ça :
<!-- URL : /mapage -->
<html>
<head>...</head>
<body>
<nav>...</nav>
<ul>
<li>Item 1</li>
...
<li>Item 10</li>
</ul>
...
<div>
Total : 125 items
</div>
...
reste de la page
...
<a href="/mapage">Mettre à jour</a>
</body>
</html>
Tu peux garder les choses comme ça. À chaque clic sur le lient "Mettre à jour", µJS va chercher la page, récupère le
<body>, et le met à la place du<body>de la page courante. Ça met à jour toute la page.Autre solution : tu ne veux mettre à jour que deux portions (la liste et le nombre total d'items), pas la page entière. Dans ce cas, tu vas mettre des instructions qui n'ont pas d'effet sur le navigateur au chargement initial, mais qui seront utilisées par µJS lors du chargement dynamique.
<!-- URL : /mapage -->
<html>
<head>...</head>
<body>
<nav>...</nav>
<ul id="liste" mu-patch-target="#liste">
<li>Item 1</li>
...
<li>Item 10</li>
</ul>
...
<div id="nombre" mu-patch-target="#nombre">
Total : 125 items
</div>
...
reste de la page
...
<a href="/mapage" mu-mode="patch">Mettre à jour</a>
</body>
</html>
Ici, le lien porte un attribut
mu-mode="patch", donc µJS va chercher à "patcher" la page. Il va récupérer le HTML en AJAX, puis il regarde ce qu'il y a dedans.Le tag avec l'attribut
mu-patch-target="#liste"va donc remplacer le tag avecid="liste"dans page actuelle .Et le tag avec l'attribut
mu-patch-target="#nombre"va remplacer le tag avecid="nombre"dans la page actuelle.Et si tu cherches à vraiment aller plus loin (mais c'est souvent inutile), tu détectes l'en-tête HTTP
X-Requested-Withpour que lors d'une requête effectuée par µJS, tu ne renvoies que ce flux :<ul id="liste" mu-patch-target="#liste">
<li>Item 1</li>
...
<li>Item 10</li>
</ul>
<div id="nombre" mu-patch-target="#nombre">
Total : 125 items
</div>
Cache HTTP : La remarque est juste, uniquement dans le tout dernier cas décris (si tu détectes
X-Requested-Withpour ne pas renvoyer la page entière). Dans ce cas, la solution standard est d'ajouterVary: X-Requested-Withdans les réponses serveur. Ça crée deux entrées de cache distinctes pour la même URL (réponse complète vs fragment).Service workers :
fetch()passe par le service worker, qui voit tous les headers. Aucun problème particulier. Le service worker peut même les utiliser pour différencier les stratégies de cache.[^] # Re: contenus et habillage
Posté par Fernando . Évalué à 2 (+1/-0).
Ok donc le backend peut savoir que c'est µJS qui l'appelle mais est obligé à minima de fournir tous les tags patchables de la page et pas éventuellement un en particulier.
Je vois un gros intérêt à ne pas mettre à jour la totalité du body est s'il contient des éléments avec lesquels l'utilisateur peut interagir : vidéo, champ input, tag detail, case à cocher, bouton radio, etc. Du coup il faut éviter de mettre ce type d'élément dans des tags patchables sinon l'effet ne sera pas top pour l'utilisateur.
EDIT: Je viens de voir que tu conseilles d'utiliser le module Idiomorph de chez HTMX pour cela.
Pour le cache effectivement puisque du coup il n'y a que 2 variantes possibles pour une même URL.
Pour le service worker ok mais il faut faire attention à reconduire tes headers si on y bricole une nouvelle requête.
[^] # Re: contenus et habillage
Posté par Amaury Bouchard (site web personnel) . Évalué à 2 (+1/-0).
"Obligé de fournir tous les tags patchables" : Non, pas du tout. En mode patch, le serveur retourne uniquement les fragments qu'il veut mettre à jour. Il n'y a pas d'obligation de retourner tous les éléments patchables de la page. Le serveur décide ce qu'il envoie, µJS applique ce qu'il reçoit.
Dans l'exemple que j'ai donné, le serveur envoie deux fragments qui vont modifier deux zones dans la page. Mais on pourrait tout aussi bien envoyer un seul fragment. Ou trois.
En fait, il n'y a pas de notion de "tag patchable". En mode patch, le flux envoyé par le serveur détermine les éléments à mettre à jour grâce aux attributs
mu-patch-targetqui servent à indiquer la cible à mettre à jour. Mais ça peut être n'importe quel élément "ciblable" de la page.Éléments interactifs dans les zones patchables : C'est ça, idiomorph est là pour ça. Il fait du diff/patch du DOM plutôt que de remplacer brutalement, ce qui préserve l'état des inputs, vidéos, checkboxes, etc.
Service worker et headers : Bonne remarque. Si le service worker crée une nouvelle requête (new Request(…)), il faut bien recopier les headers µJS manuellement, sinon le backend ne les voit plus. À toi de voir si ton backend en a vraiment besoin ou pas.
[^] # Re: contenus et habillage
Posté par Fernando . Évalué à 1 (+0/-0).
Je ne comprends pas bien comment le serveur peut décider quels fragments mettre à jour puisqu'il n'a aucun contexte, il peut juste savoir s'il est appelé par µJS ou directement par le navigateur (ou bot).
[^] # Re: contenus et habillage
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+0/-0).
Là ça dépend de ton application. Le contexte vient de tes données, et de la session utilisateur.
Regarde l'exemple que j'ai mis plus haut. Il y a deux zones à mettre à jour : la liste des items, et le nombre total d'items. Donc le serveur peut n'envoyer que ces deux fragments-là.
Mais on pourrait imaginer que, dans certains cas, le serveur envoie un troisième fragment. Par exemple un message pour dire qu'il y a une erreur sur un item. Ce fragment apparaîtrait dans une zone de la page qui est prévue pour ça, mais qui serait vide en temps normal.
Ou imaginons un tableau de bord qui affiche des données (des tableaux, des graphiques, etc.). Il y a un polling qui fait une requête toutes les minutes. À chaque fois, l'application envoie un fragment pour chaque bloc de données, uniquement si la donnée a été modifiée depuis moins d'une minute. Parfois ça ne renverra rien ; d'autres fois ça renverra un ou deux blocs ; et d'autres fois ça renverra tous les blocs de la page.
[^] # Re: contenus et habillage
Posté par Fernando . Évalué à 2 (+1/-0).
Zone qui aurait un mu-patch-target et si le serveur répondait cette zone avec quelque chose de non vide elle apparaîtrait.
Ce que tu veux dire c'est que le serveur peut ne pas répondre avec toutes les targets, seulement celles données par le serveur seront patchées.
Je présume aussi que le serveur peut répondre avec un nouvel élément ayant un mu-patch-target à condition qu'il place ce nouvel élément dans un autre élément ayant lui aussi un mu-patch-target et déjà présent dans la page, sinon il ne sera pas pris en compte.
[^] # Re: contenus et habillage
Posté par Amaury Bouchard (site web personnel) . Évalué à 0 (+0/-1).
Ah je crois que je comprends la source de la confusion.
L'attribut
mu-patch-targetne sert pas à dire "ceci est un élément qui peut être patché".C'est un attribut qu'il faut mettre sur le flux renvoyé par le serveur, pour dire "voici la définition de l'élément qu'il faut mettre à jour".
Par exemple, imagine si ta page contient ça :
En cliquant sur le lien, µJS va chercher le flux HTML disponible sur
/update.Imaginons que le serveur renvoie ça :
Ça mettra à jour l'élément de la page
#zone-1(donc l'élément qui a l'attributid="zone-1").Imaginons qu'au clic suivant sur le lien, le serveur réponde ça :
Là, ça va mettre deux fragments à jour : le tag
<header>, et l'élément qui a l'IDzone-2.Dans les exemples précédents, la page initiale (celle du premier chargement) contenait des attributs
mu-patch-target, mais ils ne sont pas utilisés. C'était pour montrer qu'on peut envoyer le même code HTML lors du premier chargement et lors des patchs.# Utiliser les "data-" attributes
Posté par Adrien Dorsaz (site web personnel, Mastodon) . Évalué à 5 (+3/-0). Dernière modification le 11 mars 2026 à 22:11.
Hello,
Pourquoi ne pas utiliser le préfixe
data-devant le nom des attributs du framework ?D'après ce que je comprend de MDN, ce préfixe est prévu pour que les applications puissent ajouter des données supplémentaires aux balises HTML sans casser la compatibilité avec HTML.
En plus, elles sont plus facile à accéder en JavaScript grâce à la propriété
.dataset. Elles sont aussi accessibles avec CSS, mais je crois que c'est le cas pour tous les attributs.Il me semblait que ce n'était pas utilisable avec htmx, mais d'après leur documentation, il est possible d'utiliser
data-htmx-*maintenant.Edit: Je viens de voir sur le README de ton repo que la syntaxe
data-mu-*est utilisable aussi. Pourquoi alors proposer par défaut d'utiliser une syntaxe non-compatible avec HTML ?[^] # Re: Utiliser les "data-" attributes
Posté par Amaury Bouchard (site web personnel) . Évalué à 2 (+1/-0).
C'est la même raison qui pousse µJS à proposer les attributs
mu-*etdata-mu-*, que celle qui a poussé htmx à proposerhx-*etdata-htmx-*.Si tu as vraiment besoin que ton code HTML passe dans un validateur HTML sans erreur, tu utilises les attributs
data-*.Par contre, pour la plupart des gens ce n'est pas une nécessité absolue, et dans ce cas on est content d'avoir un code moins verbeux. Les navigateurs gèrent cela sans le moindre problème.
On peut remarquer que µJS et htmx ne sont pas les seuls à proposer des attributs personnalisés (dont les noms ne commencent pas par
data-). C'est aussi le cas d'AlpineJS (x-data,x-show…), de VueJS (v-if,v-for…), d'Angular (ng-if,ng-click…), Svelte (on:click,bind:value…), Unpoly (up-target,up-follow…), Livewire (wire:click,wire:model…).[^] # Re: Utiliser les "data-" attributes
Posté par barmic 🦦 . Évalué à 3 (+1/-0).
Pour une partie d’entre eux, ce n’est pas à utiliser dans du HTML, mais dans des templates HTML. Le navigateur ne fais pas de rendu de contenu ayant ces tags. C’est le cas d’angular par exemple où c’est encore plus clair maintenant qu’ils utilisent des syntaxes :
(à côté de ça angular ne se gêne pas pour ajouter des attributs à lui sans
data-)https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
# Merci, ça me plait beaucoup
Posté par Luc-Skywalker . Évalué à 2 (+0/-0).
et HTMX aussi.
Sinon, tout à fait d'accord
"Si tous les cons volaient, il ferait nuit" F. Dard
[^] # Re: Merci, ça me plait beaucoup
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+0/-0).
Merci ! :)
# test sur site static
Posté par steph1978 . Évalué à 5 (+3/-0).
J'ai fait un test vite fait sur mon "blog" : une page index et une centaine de pages article.
Je voyais pas trop ce que je pouvais gagner car les pages se chargent très vite : <10ms.
J'ai juste ajouté à la main les deux balises
scriptsur la page index. Comme ensuite les page article ne sont pas rechargé mais chargées par µJS, pas besoin de le déclarer sur toutes les pages.Étonnement, je constate un plus grande fluidité. Cela vient du fait que le DOM n'est pas entièrement recréé mais simplement amendé par µJS. Et les navigateurs sont très très bons pour faire le rendu DOM.
En conséquence, cela fonctionne bien tant qu'on a pas de changement de styles. C'est la première page à charger µJS qui impose ces styles au reste du contenu.
La fluidité vient aussi du fait qu'il précharge le contenu au survol du lien, même si celui-ci n'est au final pas cliqué. Au clic, le contenu est déjà dans le cache, pas d'aller-retour réseau ; le swap de contenu est rapide.
Je confirme que l'URL est bien mise à jour dans la barre d'URL et bien inscrite dans l'historique. Que les boutons "back" and "forth" sont fonctionnels. Cependant, le titre n'est pas mis à jour et que sauter loin dans l'historique (click droit sur les boutons et choix d'une entrée) ne fonctionne pas.
D'autre part, cela ne m'est d'aucune utilité avec ma façon habituelle de naviguer qui est de garder la page index ouverte et d'utiliser le click bouton du milieu pour ouvrir un lien dans un nouvel onglet. Sur des sites
bloatedmodernes, cela permet d'ouvrir plusieurs contenus sans repasser par le chargement de la page de départ et re-cliquer les 12 popups de bienvenu, pour aller au contenu suivant. Couplé à TreeStyleTab, cela crée un contexte de navigation beaucoup plus agréable.D'autant que µJS vient avec un coût en RAM. Dans mon cas, sans µJS : 10MB, avec : 100MB ; YMMV.
Bref, tout comme pour HTMX, cela peut être intéressant pour ajouter un peu de réactivité sans basculer vers un framework JS.
[^] # Re: test sur site static
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 3 (+0/-0).
J'ai l'impression que ça révèle surtout un défaut des navigateurs.
Je m'explique. Une page web, c'est un document HTML qui contient un en-tête
<head>et un corps<body>. L'en-tête contient des méta-informations, la plus notoire étant le titre, qui est affiché dans la barre d'onglets du navigateur par exemple. Le contenu de la page, qui représente l'essentiel de son poids et du nombre de balises, est évidemment le corps.Lorsqu'on change de page, le navigateur commence à télécharger la page, et dès qu'il en a suffisamment, avant même d'avoir fini, il remplace le titre et commence à afficher le corps.
Avec µJS, la page suivante est intégralement téléchargée (à confirmer), puis le titre et le corps affiché sont remplacés. Ce fonctionnement n'a rien d'intrinsèquement plus rapide (ce serait même plutôt plus lent d'ailleurs). En revanche, c'est visiblement plus fluide, alors que, côté contenu de page, le navigateur n'a rien de très différent à faire : il remplace l'affichage d'une page par une autre.
Ça suggérerait surtout une optimisation qui pourrait être faite au sein d'un navigateur : au lieu de faire je ne sais quoi au changement de page, faire ce que fait µJS.
[^] # Re: test sur site static
Posté par steph1978 . Évalué à 2 (+0/-0).
C'est ce qui est expliqué plus haut, oui.
Seulement le body, d'après ce qui est dis dans ce même "plus haut". Ce que confirme mes tests.
[^] # Re: test sur site static
Posté par Amaury Bouchard (site web personnel) . Évalué à 1 (+0/-0).
Merci beaucoup pour ce retour d'expérience, ça m'est très utile !
Si on peut accéder directement aux pages d'articles, il faut mettre le Javascript sur toutes les pages. Ce n'est pas un souci, µJS ne le réinterprétera pas. Mais comme ça, lorsqu'on va directement sur un article, si ensuite on navigue sur une autre page on bénéficiera du chargement par µJS.
La plupart du temps, les mêmes CSS sont utilisés sur toutes les pages d'un site.
Mais si une page contient du CSS ou du JS (dans la page ou via le chargement d'un fichier externe) qui n'est pas connu, µJS va le charger.
Pour le titre, ça devrait le prendre en compte. Je vais me pencher dessus.
Pour l'historique, j'avoue ne pas avoir testé. Je vais regarder aussi.
Intéressant. C'est après avoir navigué sur plusieurs pages, ou dès le chargement de la page initiale ?
[^] # Re: test sur site static
Posté par steph1978 . Évalué à 2 (+0/-0).
Plusieurs pages. J'imagine que les éléments s'accumulent. Ce qui n'est pas choquant.
[^] # Re: test sur site static
Posté par Amaury Bouchard (site web personnel) . Évalué à 2 (+1/-0).
J'ai fais des tests et vérifié le code : le titre est bien mis à jour.
Il y a des subtilités :
Le titre n'est mis à jour que si l'historique de navigation est mis à jour. Donc un lien avec l'attribut
mu-history="false"ne verra pas la mise à jour du titre.Dans le mode patch, la mise à jour ne se fait que sur les fragments qui portent l'attribut
mu-patch-target. Donc si on veut mettre le titre à jour, il faut explicitement l'indiquer :Envoyer un commentaire
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.