{"id":4712,"date":"2026-04-23T10:34:31","date_gmt":"2026-04-23T08:34:31","guid":{"rendered":"https:\/\/www.unicoda.com\/?p=4712"},"modified":"2026-04-23T10:34:31","modified_gmt":"2026-04-23T08:34:31","slug":"freshrss-retrouver-une-interface-epuree-grace-au-mutationobserver","status":"publish","type":"post","link":"https:\/\/www.unicoda.com\/?p=4712","title":{"rendered":"FreshRSS : Retrouver une interface \u00e9pur\u00e9e gr\u00e2ce au MutationObserver"},"content":{"rendered":"\n<p>\u00c9tant en train de faire de nombreux changements dans mon homelab, tant du c\u00f4t\u00e9 de l&rsquo;infrastructure que des outils, en passant par la sauvegarde. Je m&rsquo;attarde aujourd&rsquo;hui sur un point annexe rencontr\u00e9 lors de la migration de mon instance FreshRSS.<\/p>\n\n\n\n<p>Bien occup\u00e9 par d&rsquo;autres pr\u00e9occupations ces derni\u00e8res ann\u00e9es, la mise \u00e0 jour de certains services commen\u00e7aient \u00e0 prendre du retard. En migrant FreshRSS, j&rsquo;en ai profit\u00e9 pour mettre l&rsquo;outil \u00e0 jour et partir sur la derni\u00e8re version en date. Rien \u00e0 redire, l&rsquo;outil fonctionne toujours aussi bien, fait ce qu&rsquo;on lui demande et r\u00e9ponds \u00e0 mes besoins pour le suivi de flux RSS. Par contre, un petit changement dans l&rsquo;UX\/UI m&rsquo;a tr\u00e8s vite contrari\u00e9.<\/p>\n\n\n\n<p>D\u00e9sormais, FreshRSS organise toutes les entr\u00e9es par date et regroupe donc les flux par jour, avec un ent\u00eate de s\u00e9paration. Si ce changement ne se sent pas particuli\u00e8rement en consultant le flux principal contenant les entr\u00e9es de tous mes flux RSS, le r\u00e9sultat est plut\u00f4t perturbant lorsqu&rsquo;on consulte le d\u00e9tail d&rsquo;un flux RSS en particulier. La page devient alors une alternance entre ent\u00eate de date et entr\u00e9e de flux. La majeure partie des flux que je consulte publient tr\u00e8s rarement plus d&rsquo;une entr\u00e9e par jour. Ma page se remplie d&rsquo;ent\u00eate de date qui constitue la moiti\u00e9 du contenu de la page et dont je n&rsquo;ai pas l&rsquo;utilit\u00e9.<\/p>\n\n\n\n<p>Bref, direction GitHub pour confirmer qu&rsquo;un changement a eu lieu et savoir si un contournement ou une option de configuration existe. Bingo, deux issues existent:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/FreshRSS\/FreshRSS\/discussions\/8355\" data-type=\"link\" data-id=\"https:\/\/github.com\/FreshRSS\/FreshRSS\/discussions\/8355\" target=\"_blank\" rel=\"noreferrer noopener\">Changing the group-by date #8355<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/FreshRSS\/FreshRSS\/discussions\/8358\" data-type=\"link\" data-id=\"https:\/\/github.com\/FreshRSS\/FreshRSS\/discussions\/8358\" target=\"_blank\" rel=\"noreferrer noopener\">Sorting by date without date headers in the article list? #8358<\/a><\/li>\n<\/ul>\n\n\n\n<p>Avant de passer \u00e0 la solution que j&rsquo;ai mise en place pour arriver \u00e0 quelque chose de satisfaisant, voici la situation irritante en images.<\/p>\n\n\n\n<p>Avant, un flux n&rsquo;avait que trois ent\u00eates: \u00ab\u00a0Today\u00a0\u00bb, \u00ab\u00a0Yesterday\u00a0\u00bb et \u00ab\u00a0Before yesterday\u00a0\u00bb.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/FreshRSS-screenshot.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"632\" src=\"https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/FreshRSS-screenshot-1024x632.png\" alt=\"\" class=\"wp-image-4723\" srcset=\"https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/FreshRSS-screenshot-1024x632.png 1024w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/FreshRSS-screenshot-300x185.png 300w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/FreshRSS-screenshot-768x474.png 768w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/FreshRSS-screenshot-1200x741.png 1200w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/FreshRSS-screenshot.png 1387w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><\/figure>\n\n\n\n<p>Et d\u00e9sormais, avec une ent\u00eate par jour, on obtient quelque chose comme cela:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"571\" height=\"1024\" src=\"https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-571x1024.png\" alt=\"\" class=\"wp-image-4716\" srcset=\"https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-571x1024.png 571w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-167x300.png 167w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-768x1378.png 768w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-856x1536.png 856w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image.png 914w\" sizes=\"auto, (max-width: 571px) 85vw, 571px\" \/><\/a><\/figure>\n\n\n\n<p>Quelle est donc la solution, me direz-vous ?<\/p>\n\n\n\n<p>Les discussions sur GitHub proposent d&rsquo;activer l&rsquo;extension \u00ab\u00a0User CSS\u00a0\u00bb et d&rsquo;ajouter la r\u00e8gle suivante:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> .transition {\n    display: none;\n }<\/code><\/pre>\n\n\n\n<p>Ce qui a pour effet de faire dispara\u00eetre toutes les ent\u00eates. C&rsquo;est mieux, mais insuffisant \u00e0 mon go\u00fbt. J&rsquo;ai vraiment l&rsquo;impression de perdre en lisibilit\u00e9, car je ne peux plus distinguer les entr\u00e9es r\u00e9centes (les deux derniers jours) des entr\u00e9es plus anciennes. En continuant d&rsquo;exp\u00e9rimenter avec des r\u00e8gles CSS plus complexes pour tenter d&rsquo;arriver \u00e0 une pr\u00e9sentation me convenant, j&rsquo;aper\u00e7ois un bouton permettant d&rsquo;activer l&rsquo;extension \u00ab\u00a0User JS\u00a0\u00bb. Ayant pass\u00e9 de nombreuses ann\u00e9es \u00e0 exp\u00e9rimenter et construire des solutions avec du JS, un d\u00e9but d&rsquo;id\u00e9e commence \u00e0 germer dans mon esprit. Avec l&rsquo;aide d&rsquo;un MutationObserver pour intercepter les changements du DOM, je devrais \u00eatre en mesure de d\u00e9tecter l&rsquo;ajout des ent\u00eates dans le DOM et de ne garder que les trois premi\u00e8res, retrouvant ainsi une pr\u00e9sentation proche de la version historique \u00e0 laquelle je suis habitu\u00e9.<\/p>\n\n\n\n<p>Apr\u00e8s quelques it\u00e9rations, voici donc le code que j&rsquo;utilise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const labels = &#91;'Today', 'Yesterday', 'Before yesterday'];\nlet transitionCount = 0;\n\nfunction processTransition(el) {\n  const i = transitionCount++;\n  if (i &gt;= 3) {\n    el.style.display = 'none';\n  } else {\n    const link = el.querySelector('.transition-value a:first-child');\n    if (link) {\n      link.textContent = labels&#91;i];\n    }\n  }\n}\n\nconst main = document.querySelector('main');\nmain.querySelectorAll('.transition').forEach(processTransition);\n\nconst observer = new MutationObserver((mutations) =&gt; {\n  for (const mutation of mutations) {\n    for (const node of mutation.addedNodes) {\n      if (node.nodeType !== 1) {\n        continue;\n      }\n      if (node.classList?.contains('transition')) {\n        processTransition(node);\n      } else {\n        node.querySelectorAll?.('.transition').forEach(processTransition);\n      }\n    }\n  }\n});\n\nobserver.observe(main, { childList: true, subtree: true });<\/code><\/pre>\n\n\n\n<p>Ce qui nous donne le r\u00e9sultat suivant:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"582\" height=\"1024\" src=\"https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-1-582x1024.png\" alt=\"\" class=\"wp-image-4717\" srcset=\"https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-1-582x1024.png 582w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-1-170x300.png 170w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-1-768x1351.png 768w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-1-873x1536.png 873w, https:\/\/www.unicoda.com\/wp-content\/uploads\/2026\/04\/image-1.png 932w\" sizes=\"auto, (max-width: 582px) 85vw, 582px\" \/><\/a><\/figure>\n\n\n\n<p>On constate imm\u00e9diatement que l&rsquo;information est bien plus condens\u00e9e et qu&rsquo;il y a, \u00e0 mon go\u00fbt, moins d&rsquo;espace perdu. Je me rends compte en \u00e9crivant ces lignes, que les labels que j&rsquo;utilise ne correspondent toutefois plus vraiment. En effet, dans mon exemple avec le flux xkcd, les deux entr\u00e9es les plus r\u00e9centes ont \u00e9t\u00e9 re\u00e7ues le lendemain de leur publication, mais \u00e0 deux jours d&rsquo;intervalles. Mon affichage \u00ab\u00a0Aujourd&rsquo;hui\u00a0\u00bb (\u00ab\u00a0Today\u00a0\u00bb) et \u00ab\u00a0Hier\u00a0\u00bb (\u00ab\u00a0Yesterday\u00a0\u00bb), est donc inexacte, mais correspond en tout cas \u00e0 un regroupement qui me convient. Apr\u00e8s r\u00e9flexion, je m&rsquo;oriente donc vers un d\u00e9coupage \u00ab\u00a0Latest\u00a0\u00bb, \u00ab\u00a0Previous\u00a0\u00bb et \u00ab\u00a0Older\u00a0\u00bb.<\/p>\n\n\n\n<p>Pour avoir utilis\u00e9 cette solution au quotidien depuis maintenant plusieurs semaines, je suis tout \u00e0 fait satisfait du r\u00e9sultat. Bien s\u00fbr, il y a parfois quelques clignotements dans l&rsquo;interface, le temps que le code s&rsquo;ex\u00e9cute et que les ent\u00eates soient cach\u00e9es ou que leur titre change, mais rien qui ne soit g\u00eanant.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u00c9tant en train de faire de nombreux changements dans mon homelab, tant du c\u00f4t\u00e9 de l&rsquo;infrastructure que des outils, en passant par la sauvegarde. Je m&rsquo;attarde aujourd&rsquo;hui sur un point annexe rencontr\u00e9 lors de la migration de mon instance FreshRSS. Bien occup\u00e9 par d&rsquo;autres pr\u00e9occupations ces derni\u00e8res ann\u00e9es, la mise \u00e0 jour de certains services &hellip; <a href=\"https:\/\/www.unicoda.com\/?p=4712\" class=\"more-link\">Continuer la lecture<span class=\"screen-reader-text\"> de &laquo;&nbsp;FreshRSS : Retrouver une interface \u00e9pur\u00e9e gr\u00e2ce au MutationObserver&nbsp;&raquo;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[615,13],"class_list":["post-4712","post","type-post","status-publish","format-standard","hentry","category-code","tag-freshrss","tag-javascript"],"_links":{"self":[{"href":"https:\/\/www.unicoda.com\/index.php?rest_route=\/wp\/v2\/posts\/4712","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.unicoda.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.unicoda.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.unicoda.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.unicoda.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4712"}],"version-history":[{"count":9,"href":"https:\/\/www.unicoda.com\/index.php?rest_route=\/wp\/v2\/posts\/4712\/revisions"}],"predecessor-version":[{"id":4725,"href":"https:\/\/www.unicoda.com\/index.php?rest_route=\/wp\/v2\/posts\/4712\/revisions\/4725"}],"wp:attachment":[{"href":"https:\/\/www.unicoda.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4712"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.unicoda.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4712"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.unicoda.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4712"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}