{"id":42,"date":"2007-11-30T11:05:10","date_gmt":"2007-11-30T09:05:10","guid":{"rendered":"http:\/\/nyams.planbweb.com\/blog\/2007\/11\/30\/optimisation-de-mon-code-javascript-pour-emuler-target_blank\/"},"modified":"2011-07-05T09:28:27","modified_gmt":"2011-07-05T07:28:27","slug":"optimisation-de-mon-code-javascript-pour-emuler-target_blank","status":"publish","type":"post","link":"https:\/\/nyamsprod.com\/blog\/optimisation-de-mon-code-javascript-pour-emuler-target_blank\/","title":{"rendered":"Emuler target=&#8221;_blank&#8221; avec javascript, mon howto"},"content":{"rendered":"<div class=\"message warning\">\n<p><strong>Attention:<\/strong> Les informations de ce billet sont susceptibles d'&ecirc;tre obsol&egrave;tes car vieux de plus 2 ans.<\/p>\n<p><strong>Warning: <\/strong> The information you are reading may be obsolete, this post was published more than 2 years ago.<\/p>\n<\/div><p>J&#8217;ai d\u00e9cid\u00e9 d&#8217;am\u00e9liorer mon code d&#8217;ouverture d&#8217;une page externe \u00e0 mon site. Pour cela j&#8217;ai un peu googl\u00e9 et surtout je me suis souvenu des fondamentaux du Javascript, souvent cela aide. Essayons donc de r\u00e9soudre \u00e0 nouveau le vieux probl\u00e8me de la disparition de l\u2019\u00e9l\u00e9ment <code>target<\/code> de la sp\u00e9cification <abbr title=\"eXtended HyperText Markup Language\">XHTML<\/abbr> 1.0 Strict. En attendant sa r\u00e9habilitation via le <a title=\"Le Draft du HTML 5.0\" lang=\"en\" rel=\"external\" href=\"http:\/\/www.whatwg.org\/specs\/web-apps\/current-work\/multipage\/\">HTML 5.0<\/a>, <!--more-->il existe de nombreuses techniques pour simuler le fameux <code>target=\"_blank\"<\/code> qui manque dans le XHTML, voici ma solution et, surtout le raisonnement que j&#8217;ai suivi pour atteindre mon code final.<\/p>\n<h3>Etape 0 : <em>Que d\u00e9sire-t-on faire exactement ?<\/em><\/h3>\n<p>Le principe g\u00e9n\u00e9ral de r\u00e9solution du probl\u00e8me est toujours le m\u00eame :<\/p>\n<ol>\n<li>On r\u00e9cup\u00e9re tous les liens d\u2019une page;<\/li>\n<li>On d\u00e9termine d\u2019une mani\u00e8re ou d\u2019une autre si le lien pointe vers l\u2019ext\u00e9rieur ou vers l\u2019int\u00e9rieur du site;<\/li>\n<li>Si le lien pointe hors du site on ouvre le lien dans une nouvelle page, et votre visiteur se retrouve sur cette page;<\/li>\n<li>Il faut bien sur que le code soit le plus rapide et le plus court possible;<\/li>\n<\/ol>\n<p>Pour effectuer cela on utilisera le Javascript, car m\u00eame sans Javascript les liens doivent rester fonctionnels. C\u2019est d\u2019ailleurs l\u2019une des raisons pour laquelle <code>target<\/code> a \u00e9t\u00e9 enlev\u00e9 des sp\u00e9cifications du XHTML Strict, car <code>target<\/code> n\u2019avait aucune signification hors des navigateurs \u00e9volu\u00e9s, par exemple, sur votre t\u00e9l\u00e9phone mobile, <code>target=\"_blank\"<\/code> ne sert \u00e0 rien.<\/p>\n<h3>Etape 1 : <em>La solution la plus intuitive<\/em><\/h3>\n<p>Tout d&#8217;abord, on rajoute \u00e0 <strong>toutes <\/strong>les balises <code>&lt;a&gt;<\/code> de votre page dont les liens pointent hors de votre site  <a title=\"la d\u00e9fintion de l'attribut rel et pourquoi il est important\" lang=\"fr\" rel=\"external\" href=\"http:\/\/www.la-grange.net\/w3c\/html4.01\/struct\/links.html#adef-rel\">l\u2019attribut <code>rel<\/code><\/a> avec la valeur <code>external<\/code>. Ensuite apr\u00e8s avoir effectuer se travail fastidieux, il vous suffira d&#8217;ajouter le script suivant \u00e0 votre page.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">var external;\r\nvar externalLinks=function(){\r\nif( !document.getElementsByTagName ){\r\nreturn;\r\n}\r\nvar anchorList=document.getElementsByTagName('A');\r\nfor(var i=0,anchor;anchor=aList&#x5B;i];i++){\r\nif( anchor.getAttribute('rel').indexOf('external')&gt;-1){\r\nanchor.onclick=function(){\r\nexternal=window.open(this.href,'external');\r\nexternal.focus();\r\nreturn false;\r\n};\r\n}\r\n}\r\n};\r\nwindow.onload=externaLinks;<\/pre>\n<p>Cette solution est fonctionnelle, puisqu&#8217;elle reprend fid\u00e8lement les 3 points expos\u00e9s ci-dessus, mais ce code peut et doit  \u00eatre am\u00e9liorer.<\/p>\n<h3>Etape 2 : <em>ce que l\u2019XHTML n\u2019a plus, le DOM l&#8217;a toujours<\/em><\/h3>\n<p>A la diff\u00e9rence de l\u2019XHTML, le <abbr title=\"Document Object Model : permet la repr\u00e9sentation d'un document html et son iteraction via Javascript par exemple\">DOM<\/abbr> permet toujours d&#8217;utiliser la propri\u00e9t\u00e9 <code>target<\/code> sur un lien. Nous pouvons donc faire \u00e9voluer notre fonction, et dire au revoir \u00e0 <code>window.open<\/code> et \u00e0 la variable <code>external<\/code> :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">var externalLinks=function(){\r\nif( !document.getElementsByTagName ){\r\nreturn;\r\n}\r\nvar anchorList=document.getElementsByTagName('A');\r\nfor(var i=0,anchor;anchor=anchorList&#x5B;i];i++){\r\nif( anchor.getAttribute('rel').indexOf('external')&gt;-1){\r\nanchor.target='_blank';\r\n}\r\n}\r\n};\r\nwindow.onload=externaLinks;<\/pre>\n<p>Nous avons am\u00e9lior\u00e9 notre script,  les pages s\u2019ouvriront alors comme attendues. Une recherche sur le net vous fera invariablement tomber sur une variante de ce script, c\u2019est cette fonction qui est le plus souvent utilis\u00e9e pour r\u00e9soudre notre probl\u00e8me.<\/p>\n<h3>Etape 3 : <em>DOM level 1 ou DOM level 0 que choisir ?<\/em><\/h3>\n<p>Le gros point faible des 2 techniques d\u00e9crites ci-dessus et qu\u2019elles se basent toutes sur des m\u00e9thodes du <code><a title=\"un tutorial sur le DOM level 1\" lang=\"fr\" href=\"http:\/\/nyams.planbweb.com\/tutorial\/dom\/\">DOM level 1<\/a> <\/code><code>getElementsByTagName<\/code> et <code>getAttribute<\/code>. Si on poss\u00e8de un navigateur qui ne g\u00e8re pas ce niveau de <code>DOM<\/code> on est un peu, voire beaucoup \u00e0 la ramasse, de plus , et c&#8217;est le plus important ces m\u00e9thodes sont relativement lentes. Qu\u2019\u00e0 cela ne tienne, en lisant les sp\u00e9cifications du DOM level 0 j\u2019ai ressorti du cong\u00e9lateur <code>document.links<\/code> l\u2019objet qui g\u00e8re les liens en <code>DOM level 0<\/code>, notre script devient alors :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">var externalLinks=function(){\r\nvar anchorList=document.links;\r\nfor(var i=0,anchor;anchor=anchorList&#x5B;i];i++){\r\nif( anchor.rel.indexOf('external')&gt;-1){\r\nanchor.target='_blank';\r\n}\r\n}\r\n};\r\nwindow.onload=externaLinks;<\/pre>\n<p>Et voil\u00e0, maintenant mon script fonctionnera m\u00eame sous Netscape 4\/IE 4. Le script est donc devenu  <code>DOM level 1<\/code> Ind\u00e9pendant et il a gagn\u00e9 en rapidit\u00e9.<\/p>\n<h3>Etape 4 : <em>window.location \u00e0 la rescousse<\/em><\/h3>\n<p>Continuons \u00e0 parcourir la classe <code>document.links<\/code>. Certaines de ses propri\u00e9t\u00e9s et de ses m\u00e9thodes sont identiques \u00e0 ceux de la classe <a title=\"les propri\u00e9t\u00e9s et les m\u00e9thodes de la classe window.location\" lang=\"fr\" href=\"\/qsparser.html\"><code>window.location<\/code><\/a>. Gr\u00e2ce \u00e0 cela on peut donc obtenir pour chaque lien des informations d\u00e9taill\u00e9es sur le domaine point\u00e9, donc l&#8217;obligation de r\u00e9cup\u00e9rer l\u2019attribut <code>rel<\/code> n&#8217;est plus de mise, si le domaine du lien est diff\u00e9rent du domaine sur lequel se trouve  la page qui appelle le script alors on ouvre une nouvelle fenetre, notre script se simplifie encore est devient:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">var externalLinks=function(){\r\nvar anchorList=document.links;\r\nvar myhost=window.location.hostname;\r\nfor(var i=0,anchor;anchor=anchorList&#x5B;i];i++){\r\nif( anchor.hostname != myhost ){\r\nanchor.target='_blank';\r\n}\r\n}\r\n};\r\nwindow.onload=externaLinks;<\/pre>\n<p>Vous vous rappelez le travail fastidieux pour rajouter votre attribut <code>rel<\/code>, et bien gr\u00e2ce \u00e0 ce nouveau script, vous n\u2019avez plus besoin de le faire. Personnellement je continue \u00e0 remplir l\u2019attribut <code>rel<\/code>, mais il n\u2019est plus obligatoire pour notre script.<\/p>\n<p>Noter que pour acc\u00e9l\u00e9rer mon script je d\u00e9clare 2 variables <code>anchorLinks <\/code>et <code>myhost<\/code>. Si je fais cela c&#8217;est pour cacher les r\u00e9sultats de <code>document.links<\/code> et de <code>window.location.hostname<\/code>. Ces variables permettent au navigateur de ne pas recalculer les propri\u00e9t\u00e9s de  ces classes \u00e0 chaque it\u00e9ration dans la boucle <code>for<\/code>. J&#8217;augmente ainsi la rapidit\u00e9 de mon script.<\/p>\n<h3>Etape 5 : <em>bye bye window.onload<\/em><\/h3>\n<p>Maintenant que notre fonction <code>externalLinks<\/code> est fonctionnelle et optimis\u00e9e pour tout navigateur qui connait au moins  <code>document.links<\/code> et <code>window.location<\/code>, occupons-nous de son \u00e9x\u00e9cution par le navigateur et donc de <code>window.onload<\/code>. Cette m\u00e9thode de la classe <code>window <\/code>permet d\u2019activer notre fonction quand la page termine le chargement de tous les objets multim\u00e9dia dont elle a besoin, hors cela repr\u00e9sente un double handicap. Si vous avez dans votre page une autre fonction qui est \u00e9galement d\u00e9clench\u00e9e par <code>window.onload<\/code> il y aura conflit entre vos fonctions. Un moyen de contourner ce premier probl\u00e8me est d\u2019utiliser la fonction <a title=\"l'article de base de la fonction\" lang=\"en\" rel=\"external\" href=\"http:\/\/simonwillison.net\/2004\/May\/26\/addLoadEvent\/\"><code>addLoadEvent<\/code> de Simon Willinson<\/a> , mais cela ne fait que d\u00e9placer le probl\u00e8me. Si notre document contient beaucoup d\u2019objets \u00e0 charger, vous serez d\u00e9pendant du temps de chargement total de ces objets avant que la fonction ne s\u2019active. Dans certains cas le visiteur pourrait m\u00eame d\u00e9j\u00e0 cliquer sur vos liens externes alors que votre script n\u2019a pas encore tourner, donc <code>window.onload<\/code> ne r\u00e9pond pas nos besoins.<br \/>\nUne autre solution serait d\u2019utiliser la fonction <a title=\"l'histoire de DOMloaded commence ici\" lang=\"en\" rel=\"external\" href=\"http:\/\/dean.edwards.name\/weblog\/2005\/09\/busted\/\"><code>DOMloaded<\/code> de Dean Edwards<\/a> qui permet de lancer notre fonction d\u00e8s la fin du chargement de l\u2019arbre DOM dans la m\u00e9moire du navigateur. Mais cette fonction d\u00e9pend partiellement du DOM level 2, donc notre code perdrait en portabilit\u00e9, donc cette solution est \u00e9galement \u00e0 abandonner.<br \/>\nFinalement on peut tout simplement <strong>placer notre script en fin de page<\/strong> juste avant la balise de fermeture de <code>body<\/code> et demander son \u00e9xecution directe. Ce qui nous donne le code suivant \u00e0 placer en fin de page :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">var externalLinks=function(){\r\nvar anchorList=document.links;\r\nvar myhost=window.location.hostname;\r\nfor(var i=0,anchor;anchor=anchorList&#x5B;i];i++){\r\nif( anchor.hostname != myhost ){\r\nanchor.target='_blank';\r\n}\r\n}\r\n};\r\nexternalLinks();<\/pre>\n<h3>Etape 6 : <em>Javascript c&#8217;est magnifique<\/em><\/h3>\n<p>J&#8217;aurais pu m&#8217;arr\u00e9ter l\u00e0, mais la recherche d&#8217;encore plus d&#8217;efficacit\u00e9 me faire me rappeler <a title=\"Ce que l'on peut faire avec la boucle for en Javascript\" lang=\"fr\" rel=\"external\" href=\"http:\/\/developer.mozilla.org\/fr\/docs\/Guide_JavaScript_1.5:Boucles:L'instruction_for\">la d\u00e9finition de la boucle for<\/a> en Javascript et cela me permet d&#8217;initialiser nos variables directement dans la boucle, de plus je connais une mani\u00e8re d\u2019ex\u00e9cuter le Javascript de fa\u00e7on direct en utilisant le <a title=\"le Block Scope Expliqu\u00e9\" href=\"http:\/\/weblog.raganwald.com\/2007\/08\/block-structured-javascript.html\">Block Scope<\/a>. l&#8217;utilisation de toutes ces techniques me permet d&#8217;encore plus modifier mon code d&#8217;invocation qui devient d\u00e8s lors :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">(function(){\r\nfor(var anchorList=document.links,myhost=window.location.hostname,i=0,anchor;anchor=anchorList&#x5B;i];i++){\r\nif(anchor.hostname!=myhost){\r\nanchor.target='_blank';\r\n}\r\n}\r\n})();<\/pre>\n<p>Et si on diminue la longueur des noms des variables et on met tout sur une ligne on obtient in fine:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">(function(){for(var l=document.links,h=window.location.hostname,i=0,a;a=l&#x5B;i];i++){if(a.hostname!=h){a.target='_blank';}}})();<\/pre>\n<p>et voila\u2026 \ud83d\ude42  vous avez devant vous la fonction  que j&#8217;utilise en ce moment pour mon site. C&#8217;est pas magnifique \u00e7a !!!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ma solution pour \u00e9muler target=&#8221;_blank&#8221; avec du javascript. Je ne vais pas r\u00e9-inventer la roue juste appliquer les r\u00e8gles de bases du javascript pour rendre mon code le plus efficace, le plus portable et le plus rapide possible, suivez le guide. <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[5],"tags":[46,163,253,292],"class_list":["post-42","post","type-post","status-publish","format-standard","hentry","category-web","tag-astuce","tag-dom","tag-html","tag-javascript"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/posts\/42","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/comments?post=42"}],"version-history":[{"count":4,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/posts\/42\/revisions"}],"predecessor-version":[{"id":1607,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/posts\/42\/revisions\/1607"}],"wp:attachment":[{"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/media?parent=42"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/categories?post=42"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/tags?post=42"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}