{"id":2713,"date":"2023-03-18T13:22:00","date_gmt":"2023-03-18T12:22:00","guid":{"rendered":"https:\/\/jagullo.fr\/blog\/?p=2713"},"modified":"2023-03-19T21:40:37","modified_gmt":"2023-03-19T20:40:37","slug":"optimiser-les-selecteurs-en-javascript-et-css","status":"publish","type":"post","link":"https:\/\/jagullo.fr\/blog\/optimiser-les-selecteurs-en-javascript-et-css\/","title":{"rendered":"Optimiser les s\u00e9lecteurs en JavaScript et CSS"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Pr\u00e9sentation des s\u00e9lecteurs<\/h2>\n\n\n\n<p>Un <strong>s\u00e9lecteur JavaScript ou CSS<\/strong> est une m\u00e9thode pour identifier un \u00e9l\u00e9ment situ\u00e9 dans une page HTML. Pour appliquer un style \u00e0 un \u00e9l\u00e9ment, il faut donc l&rsquo;appeler avec un s\u00e9lecteur dans son code CSS. En JavaScript toutes les balises sont ajout\u00e9es dans le DOM qui est une repr\u00e9sentation structur\u00e9e de la page. Dans le m\u00eame principe que le CSS, pour appeler une balise HTML dans un code JS, il faut l&rsquo;identifier \u00e0 partir d&rsquo;un s\u00e9lecteur. JavaScript dispose de plusieurs fonctions pour cela comme document.getElementById() ou document.querySelector(). Si on utilise la librairie jQuery, on peut appeler une balise HTML directement avec $(\u00ab\u00a0.ma-classe\u00a0\u00bb) puis appliquer une fonction \u00e0 l&rsquo;objet cibl\u00e9.<\/p>\n\n\n\n<p>Pour <strong>mesurer les performances<\/strong> d&rsquo;un s\u00e9lecteur optimis\u00e9 avec ou sans la librairie jQuery, des tests sont r\u00e9alis\u00e9s tout au long de l&rsquo;article. Cependant il n&rsquo;est pas possible ou tr\u00e8s difficile de mesurer cela au niveau du CSS. En analysant la performance au niveau du JavaScript cela donne une id\u00e9e globale dont les r\u00e9percussions pour du CSS semblent \u00eatre \u00e9videntes. Au niveau de la console des outils de d\u00e9veloppement du navigateur, on retrouve tous les r\u00e9sultats des tests d\u00e9crits dans cette page web. Ces benchmarks permettent de comparer un s\u00e9lecteur optimis\u00e9 ou non et en plus de mesurer cela avec jQuery.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/jagullo.fr\/blog\/wp-content\/uploads\/2023\/02\/illustration_benchmark-selector.jpg\" alt=\"Optimisation des s\u00e9lecteurs en JavaScript et CSS\" class=\"wp-image-2973\" width=\"720\" height=\"480\" srcset=\"https:\/\/jagullo.fr\/blog\/wp-content\/uploads\/2023\/02\/illustration_benchmark-selector.jpg 960w, https:\/\/jagullo.fr\/blog\/wp-content\/uploads\/2023\/02\/illustration_benchmark-selector-300x200.jpg 300w, https:\/\/jagullo.fr\/blog\/wp-content\/uploads\/2023\/02\/illustration_benchmark-selector-768x512.jpg 768w\" sizes=\"(max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">Optimiser les s\u00e9lecteurs en JavaScript et CSS<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Les s\u00e9lecteurs de type et d&rsquo;attribut<\/h2>\n\n\n\n<p>Pour manipuler les \u00e9l\u00e9ments d&rsquo;une page web, il est essentiel de conna\u00eetre <strong>les s\u00e9lecteurs en JavaScript<\/strong>. Un s\u00e9lecteur permet d&rsquo;identifier un \u00e9l\u00e9ment du DOM en fonction du type (div, span, a, etc.) ou d&rsquo;un attribut (id, class, href, etc.). L&rsquo;optimisation des s\u00e9lecteurs est importante pour \u00e9viter de devoir parser le DOM en entier afin d&rsquo;identifier un \u00e9l\u00e9ment. En effet, si on doit cibler un div qui poss\u00e8de une certaine classe et qu&rsquo;on sait qu&rsquo;il est situ\u00e9 dans le footer de la page, il est important d&rsquo;ajouter le footer dans le s\u00e9lecteur pour le cibler plus rapidement. On retrouve exactement le m\u00eame principe en CSS dans la d\u00e9claration de ses styles pour optimiser les performances de construction de la page.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>&lt;srcipt&gt;\n$(document).ready(function() {\n    console.log('Benchmark : simple s\u00e9lecteur');\n\n    \/\/ JS : 0.136 ms\n    console.time('test1');\n    let test1 = document.querySelector('h1.entry-title');\n    console.timeEnd('test1');\n\n    \/\/ JS (optimis\u00e9) : 0.028 ms\n    console.time('test2');\n    let test2 = document.querySelector('#main-content .post-header h1.entry-title');\n    console.timeEnd('test2');\n\n    \/\/ jQuery : 0.344 ms\n    console.time('test3');\n    let test3 = $('h1.entry-title');\n    console.timeEnd('test3');\n\n    \/\/ jQuery (optimis\u00e9) : 0.139 ms\n    console.time('test4');\n    let test4 = $('#main-content .post-header h1.entry-title');\n    console.timeEnd('test4');\n\n});\n&lt;\/srcipt&gt;<\/code><\/pre>\n\n\n\n<p>Les exemples avec optimisation du s\u00e9lecteur montre bien<strong> une performance 5 \u00e0 10x plus rapide<\/strong> que ce soit en JavaScript ou avec jQuery. Il permet de cibler le titre de la page en indiquant au navigateur dans quelle section du code source HTML il peut le trouver. L&rsquo;avantage principal est d&rsquo;aider le navigateur a identifier l&rsquo;\u00e9l\u00e9ment cibl\u00e9 mais le principal d\u00e9savantage est que si on change la structure de l&rsquo;HTML alors le s\u00e9lecteur ne fonctionne plus. Il faut trouver un bon compromis entre un s\u00e9lecteur cibl\u00e9 sans toutefois ajouter trop d&rsquo;\u00e9l\u00e9ments. Ajouter 2 \u00e0 3 \u00e9l\u00e9ments parents dans le s\u00e9lecteur est un bon ratio. Ce test montre bien qu&rsquo;un s\u00e9lecteur optimis\u00e9 en JavaScript est en moyenne 5 \u00e0 10x plus rapide.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Les multis\u00e9lecteurs<\/h2>\n\n\n\n<p>Le benchmark <strong>des multis\u00e9lecteurs<\/strong> va cibler les \u00e9l\u00e9ments de la liste des articles r\u00e9cents au niveau de la sidebar puis faire une boucle pour ajouter une classe \u00e0 chaque lien de la liste. Cet exemple permet de comparer plusieurs fonctionnalit\u00e9s comme la boucle each et l&rsquo;ajout d&rsquo;une classe. Le CSS est par d\u00e9faut multis\u00e9lecteur, car quand on cible un \u00e9l\u00e9ment dans le document, ce sont tous les \u00e9l\u00e9ments identiques qui sont cibl\u00e9s. Au final, comme le benchmark pr\u00e9c\u00e9dent, JavaScript natif est <strong>2 \u00e0 3x plus rapide<\/strong> que jQuery pour cibler plusieurs balises et r\u00e9aliser une boucle. Si on optimise un peu son s\u00e9lecteur, on a un gain de performance de <strong>1,5x<\/strong> par rapport \u00e0 une s\u00e9lection directe de l&rsquo;objet.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>&lt;srcipt&gt;\n$(document).ready(function() {\n    console.log('Benchmark : multi s\u00e9lecteurs');\n\n    \/\/ JS : 0.636 ms\n    console.time('test5');\n    let test5 = document.querySelectorAll('a.wp-block-latest-posts__post-title');\n    test5.forEach(element =&gt; element.classList.add('new-class-js'));\n    console.timeEnd('test5');\n\n    \/\/ JS (optimis\u00e9) : 0.421 ms\n    console.time('test6');\n    let test5 = document.querySelectorAll('#main-content .et_pb_extra_column_sidebar a&#91;href]');\n    test5.forEach(element =&gt; element.classList.add('new-class-js-optim'));\n    console.timeEnd('test6');\n\n    \/\/ jQuery (optimis\u00e9) : 0.826 ms\n    console.time('test7');\n    let test6 = $('#main-content .et_pb_extra_column_sidebar a&#91;href]');\n    test6.each(function() { $(this).addClass('new-class-jquery'); });\n    console.timeEnd('test7');\n\n});\n&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Les s\u00e9lecteurs first &amp; last child<\/h2>\n\n\n\n<p>Ces s\u00e9lecteurs permettent de cibler <strong>le premier et dernier \u00e9l\u00e9ment <\/strong>d&rsquo;une liste ou d&rsquo;un ensemble de m\u00eame type. Ils sont tr\u00e8s pratiques en CSS pour appliquer un style particulier au premier ou au dernier \u00e9l\u00e9ment d&rsquo;une liste ou d&rsquo;un tableau. Comme dans les tests pr\u00e9c\u00e9dents un s\u00e9lecteur optimis\u00e9 en JavaScript natif est <strong>2 \u00e0 3x plus rapide<\/strong> pour cibler l&rsquo;\u00e9l\u00e9ment. Il existe aussi des fonctions JavaScript et jQuery pour cibler ces \u00e9l\u00e9ments comme lastElementChild et last mais utiliser les s\u00e9lecteurs qui le font de fa\u00e7on tr\u00e8s bien est plus propre.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>&lt;script&gt;\n$(document).ready(function() {\n    console.log('Benchmark : s\u00e9lecteurs first &amp; last child');\n\n    \/\/ JS : 0.193 ms\n    console.time('test8');\n    let test8 = document.querySelector('ul.wp-block-latest-posts li:last-child');\n    console.timeEnd('test8');\n\n    \/\/ JS (optimis\u00e9) : 0.065 ms\n    console.time('test9');\n    let test9 = document.querySelector('#main-content .et_pb_extra_column_sidebar ul.wp-block-latest-posts li:last-child');\n    console.timeEnd('test9');\n\n    \/\/ jQuery (optimis\u00e9) : 0.109 ms\n    console.time('test10');\n    let test10 = $('#main-content .et_pb_extra_column_sidebar ul.wp-block-latest-posts li:last-child');\n    console.timeEnd('test10');\n\n});\n&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Le s\u00e9lecteur not<\/h2>\n\n\n\n<p>Enfin pour finir ces benchmarks de performance, <strong>le s\u00e9lecteur not<\/strong> va permettre de cibler au niveau des liens vers les r\u00e9seaux sociaux en bas de l&rsquo;article tous les r\u00e9seaux sauf Twitter. Le s\u00e9lecteur not permet de retirer un \u00e9l\u00e9ment d&rsquo;une liste en fonction d&rsquo;un attribut. Pour cela l&rsquo;attribut data-network-name servira \u00e0 cibler tous les boutons sauf celui qui a la valeur twitter. not est pratique en CSS pour appliquer des styles cibl\u00e9s sur des balises HTML. L\u00e0 encore le JavaScript en natif est <strong>2 \u00e0 3x plus rapide<\/strong> qu&rsquo;un s\u00e9lecteur non optimis\u00e9 ou que la librairie jQuery. <\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>&lt;script&gt;\n$(document).ready(function() {\n    console.log('Benchmark : s\u00e9lecteur not');\n\n    \/\/ JS : 0.212 ms\n    console.time('test11');\n    let test11 = document.querySelectorAll('a.social-share-link:not(&#91;data-network-name=\"twitter\"])');\n    console.timeEnd('test11');\n\n    \/\/ JS (optimis\u00e9) : 0.079 ms\n    console.time('test12');\n    let test12 = document.querySelectorAll('#main-content .post-footer a.social-share-link:not(&#91;data-network-name=\"twitter\"])');\n    console.timeEnd('test12');\n\n    \/\/ jQuery (optimis\u00e9) : 0.147 ms\n    console.time('test13');\n    let test13 = $('#main-content .post-footer a.social-share-link:not(&#91;data-network-name=\"twitter\"])');\n    console.timeEnd('test13');\n\n});\n&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion sur les tests de performance<\/h2>\n\n\n\n<p>Pour conclure ces benchmarks, on peut remarquer qu&rsquo;un s\u00e9lecteur optimis\u00e9 en JavaScript natif est <strong>en moyenne 2 \u00e0 5x plus rapide<\/strong> pour identifier un \u00e9l\u00e9ment. La librairie jQuery offre un bon compromis et reste tout de m\u00eame relativement optimis\u00e9e. <\/p>\n\n\n\n<p>En fonction de ce que l&rsquo;on recherche dans un projet, int\u00e9grer la librairie jQuery permet d&rsquo;\u00e9crire un code plus concis et facile \u00e0 maintenir. Cependant si on cherche la performance avant tout il est judicieux d&rsquo;\u00e9crire son code en vanilla JS pour maximiser les performances de l&rsquo;application. Il faudrait comparer des fonctionnalit\u00e9s plus pouss\u00e9es, mais un d\u00e9veloppement en natif sera<strong> toujours plus rapide<\/strong> que n&rsquo;importe quelle librairie que ce soit jQuery ou une autre. Ne pas confondre une librairie qui ajoute des fonctionnalit\u00e9s \u00e0 un langage de programmation avec <a href=\"https:\/\/jagullo.fr\/blog\/choisir-un-framework-de-developpement-web-ou-mobile\/\">un web framework<\/a> qui lui permet de structurer son code et de b\u00e9n\u00e9ficier de fonctionnalit\u00e9s majeures telles que le routage, la r\u00e9activit\u00e9 ou le multilingue comme Angular ou VueJS.<\/p>\n\n\n\n<p>Il existe <strong>de nombreux types de s\u00e9lecteurs <\/strong>en CSS et JavaScript comme les combinateurs (>), les s\u00e9lecteurs adjacents (+), etc. Savoir bien les utiliser permet de manipuler le document de fa\u00e7on pr\u00e9cise et rapide. Il est pr\u00e9f\u00e9rable de privil\u00e9gier les fonctions natives de JavaScript pour rechercher une ou plusieurs balises dans une page HTML en sp\u00e9cifiant 2 \u00e0 3 \u00e9l\u00e9ments parents pour acc\u00e9l\u00e9rer le s\u00e9lecteur.<\/p>\n\n\n\n<script>\n$(document).ready(function() {\n    console.log('Benchmark : simple s\u00e9lecteur');\n    console.time('test1');\n    let test1 = document.querySelector('h1.entry-title');\n    console.timeEnd('test1');\n    console.time('test2');\n    let test2 = document.querySelector('#main-content .post-header h1.entry-title');\n    console.timeEnd('test2');\n    console.time('test3');\n    let test3 = $('h1.entry-title');\n    console.timeEnd('test3');\n    console.time('test4');\n    let test4 = $('#main-content .post-header h1.entry-title');\n    console.timeEnd('test4');\n    console.log('---------');\n\n    console.log('Benchmark : multi s\u00e9lecteurs');\n    console.time('test5');\n    let test5 = document.querySelectorAll('a.wp-block-latest-posts__post-title');\n    test5.forEach(element => element.classList.add('new-class-js'));\n    console.timeEnd('test5');\n    console.time('test6');\n    let test6 = document.querySelectorAll('#main-content .et_pb_extra_column_sidebar a[href]');\n    test6.forEach(element => element.classList.add('new-class-js'));\n    console.timeEnd('test6');\n    console.time('test7');\n    let test7 = $('#main-content .et_pb_extra_column_sidebar a[href]');\n    test7.each(function() {$(this).addClass('new-class-jquery');});\n    console.timeEnd('test7');\n    console.log('---------');\n\n    console.log('Benchmark : s\u00e9lecteurs first & last child');\n    console.time('test8');\n    let test8 = document.querySelector('ul.wp-block-latest-posts li:last-child');\n    console.timeEnd('test8');\n    console.time('test9');\n    let test9 = document.querySelector('#main-content .et_pb_extra_column_sidebar ul.wp-block-latest-posts li:last-child');\n    console.timeEnd('test9');\n    console.time('test10');\n    let test10 = $('#main-content .et_pb_extra_column_sidebar ul.wp-block-latest-posts li:last-child');\n    console.timeEnd('test10');\n    console.log('---------');\n\n    console.log('Benchmark : s\u00e9lecteur not');\n    console.time('test11');\n    let test11 = document.querySelectorAll('a.social-share-link:not([data-network-name=\"twitter\"])');\n    console.timeEnd('test11');\n    console.time('test12');\n    let test12 = document.querySelectorAll('#main-content .post-footer a.social-share-link:not([data-network-name=\"twitter\"])');\n    console.timeEnd('test12');\n    console.time('test13');\n    let test13 = $('#main-content .post-footer a.social-share-link:not([data-network-name=\"twitter\"])');\n    console.timeEnd('test13');\n});\n<\/script>\n\n\n\n<link rel=\"stylesheet\" href=\"\/blog\/wp-content\/themes\/Extra-child\/assets\/css\/prism.css\">\n<script src=\"\/blog\/wp-content\/themes\/Extra-child\/assets\/js\/prism.js\"><\/script>\n<div class=\"list-tags\"><a href=\"https:\/\/jagullo.fr\/blog\/tag\/javascript\/\" class=\"link-tag\" title=\"JavaScript\">JavaScript<\/a><a href=\"https:\/\/jagullo.fr\/blog\/tag\/jquery\/\" class=\"link-tag\" title=\"jQuery\">jQuery<\/a><\/div>","protected":false},"excerpt":{"rendered":"<p>Pr\u00e9sentation des s\u00e9lecteurs Un s\u00e9lecteur JavaScript ou CSS est une m\u00e9thode pour identifier un \u00e9l\u00e9ment situ\u00e9 dans une page HTML. Pour appliquer un style \u00e0 un \u00e9l\u00e9ment, il faut donc l&rsquo;appeler avec un s\u00e9lecteur dans son code CSS. En JavaScript toutes les balises sont ajout\u00e9es dans le DOM qui est une repr\u00e9sentation structur\u00e9e de la [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2973,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"categories":[8],"tags":[33,38],"class_list":["post-2713","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developpement","tag-javascript","tag-jquery","et-has-post-format-content","et_post_format-et-post-format-standard"],"_links":{"self":[{"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/posts\/2713","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/comments?post=2713"}],"version-history":[{"count":2,"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/posts\/2713\/revisions"}],"predecessor-version":[{"id":3913,"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/posts\/2713\/revisions\/3913"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/media\/2973"}],"wp:attachment":[{"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/media?parent=2713"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/categories?post=2713"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jagullo.fr\/blog\/wp-json\/wp\/v2\/tags?post=2713"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}