Ouvrir un lien dans une nouvelle fenêtre en quelques lignes de jQuery

J’ai toujours trouvé qu’il était du ressort de l’utilisateur de choisir s’il voulait (ou pas) ouvrir un lien dans une nouvelle fenêtre (ou un nouvel onglet). Jakob Nielsen pense d’ailleurs la même chose et l’a même intégré dans un article où il liste 10 erreurs fréquentes sur un site, article qu’il a publié en… 1999 !

Par contre, je peux totalement comprendre quand on me demande de forcer l’ouverture dans une nouvelle fenêtre. En effet, la plupart du temps, l’utilisateur ne sait pas qu’il a le choix… Ce serait trop bête de le perdre juste parce qu’il a ouvert un lien vers un autre site dans la fenêtre courante ! (je sens que ça va générer un débat)

D’un point de vue programmatique, depuis des années, l’usage pour ouvrir un lien dans une nouvelle fenêtre est d’utiliser l’attribut target="_blank" sur la balise <a>. Malheureusement, et nous le savons tous, cet attribut a été déprécié il y a plusieurs années, en même temps que les cadres (ou frames) et provoque donc une erreur à la validation W3C.

Il a donc fallu ruser et faire appel à JavaScript pour s’en sortir. En utilisant l’attribut onclick sur un lien, on a pu arriver à nos fins de façon non-intrusive :

<a href="http://www.google.com" onclick="window.open(this.href);return false;">Go to Google</a>

Ca fonctionne très bien ! Cependant, ajouter les événements directement sur les éléments n’est pas une pratique très recommandée. Mieux vaut tout placer dans un fichier JavaScript séparé.

Dans ce cas, il faut trouver un moyen de repérer les liens qu’on souhaite ouvrir dans une nouvelle fenêtre. Pour cela, la pratique plus ou moins courante dans le monde des développeurs Web est d’utiliser l’attribut rel="external" sur la balise <a>.

De cette façon, le tour est joué en quelques lignes de jQuery :

$('a[rel="external"]').click(function() {
	window.open($(this).attr('href'));
	return false;
});

Parfait. Cependant, cette technique pose problème dans certains cas…

Premièrement, imaginez que vous ayez un site déjà existant avec des milliers de pages. Vous vous voyez placer l’attribut rel="external" sur tous les liens du site ?

Deuxièmement, si votre site est un CMS, vous ne pouvez pas vous permettre d’imposer à la personne responsable du contenu de placer l’attribut rel="external" sur chacun les liens qu’elle introduirait dans le contenu du site…

Pour parer ces cas particuliers, j’ai donc écrit un petit bout de script en jQuery qui ne nécessite aucune intervention sur le code HTML :

$('a').click(function() {
	var href = $(this).attr('href');
	if (href.indexOf('http://') != -1 || href.indexOf('https://') != -1) {
		var host = href.substr(href.indexOf(':')+3);
		if (host.indexOf('/') != -1) {
			host = host.substring(0, host.indexOf('/'));
		}
		if (host != window.location.host) {
			window.open(href);
			return false;
		}
	}
});

Ce script va systématiquement ouvrir tous les liens vers des sites externes dans une nouvelle fenêtre. Dans un premier temps, on regarde si l’attribut href du lien commence par http:// ou https://. Si ce n’est pas le cas, on sait déjà que c’est un lien interne. Ensuite, on regarde si l’hôte du lien (variable host) est identique à l’hôte de la page courante (window.location.host). Si ce n’est pas le cas, on ouvre le lien dans une nouvelle fenêtre.

C’est aussi simple que ça !

34 Comments

  1. PaKaL's avatar PaKaL says:

    J’aime! C’est une bonne parade au fait que je n’ai jamais compris le pourquoi de le dépreciation du “target=” 🙂

    Like

  2. Grisha7's avatar Grisha7 says:

    J’utilise toujours la balise target=”_blank” mais pourquoi pas se mettre à jour.
    Merci Vinch pour cet article et ce bout de code 😉

    Like

  3. Matthiou's avatar Matthiou says:

    On peut être plus subtil dans l’ouverture de la nouvelle fenêtre en spécifiant quelques options :

    — afficher ou pas la location bar et les scrollbars
    — autoriser le redimensionnement
    — fixer la hauteur et largeur de la nouvelle fenêtre ( inférieures à celles de la fenêtre originale pour qu’elle reste accessible )
    — fixer la position de la fenêtre ( à nouveau pour garder la fenêtre originale partiellement visible et accessible )

    // ouvrir une fenêtre
    // et fixer ses options
    var newWindow = window.open (href,
    “”,
    “width=800,height=600”)
    ;
    // déplacer la fenêtre
    newWindow.moveTo(40,40);

    Je laisse au lecteur le plaisir de l’arithmétique des tailles et positions 😉

    Like

  4. vinch's avatar Vinch says:

    Ouais mais dans ce cas là, on parle de popup et plus vraiment de nouvelle fenêtre.
    Personnellement, je ne suis pas fan de ça mais pourquoi pas…

    Like

  5. MrThieu's avatar MrThieu says:

    A mettre bien au chaud dans les copions.
    Un grand merci à Vinch!

    PS : j’utilisais le target jusque maintenant ^^

    Like

  6. Matthiou's avatar Matthiou says:

    Qu’est-ce qui distingue un pop-up d’une nouvelle fenêtre? D’après moi rien.

    Si par pop-up on entend “une fenêtre avec des informations contextuelles qui sera refermée immédiatement après lecture”, alors il est nettement plus propre de transformer par javascript le lien href en création d’un DIV flottant au-dessus de la page courante.

    Ce DIV créé à la volée doit être muni d’un lien de fermeture et/ou d’un délai de disparition et/ou de mouseout.

    Pour finir, +1 à la première phrase de ce post : “J’ai toujours trouvé qu’il était du ressort de l’utilisateur de choisir s’il voulait (ou pas) ouvrir un lien dans une nouvelle fenêtre (ou un nouvel onglet).”

    Like

  7. vinch's avatar Vinch says:

    J’aime pas trop l’idée de priver l’utilisateur de ses barres d’outil de façon obligatoire et irréversible. De même, je déteste les sites qui redimensionnent ma fenêtre sans me demande mon avis. Je peux comprendre que la nuance entre fenêtre et popup est sans doute un peu tirée par les cheveux 😉

    Like

  8. Matthiou's avatar Matthiou says:

    Il n’est pas question de priver l’utilisateur de quoi que ce soit puisque les options de window.open permettent à peu près tout (y compris de positionner la fenêtre, ce que j’avais oublié) :

    height : la hauteur de la fenêtre (exprimée en pixels) ;
    width : la largeur de la fenêtre (exprimée en pixels) ;
    top : la position verticale de la fenêtre par rapport au bord supérieur de l’écran (exprimée en pixels) ;
    left : la position horizontale de la fenêtre par rapport au bord gauche de l’écran (exprimée en pixels) ;
    toolbar : la présence de la barre d’outils (à l’aide d’un booléen : yes ou no) ;
    location : la présence de la barre d’adresse (à l’aide d’un booléen : yes ou no) ;
    menubar : la présence de la barre de menu (à l’aide d’un booléen : yes ou no) ;
    resizable : la possibilité de redimensionner la fenêtre (à l’aide d’un booléen : yes ou no) ;
    scrollbars : la présence des scrollbars ou ascenseurs (à l’aide d’un booléen : yes ou no) ;
    status : la présence de la barre de statut (à l’aide d’un booléen : yes ou no).

    // spécifier les options
    var options = “location=1, resizable=1, scrollbars=1, toolbar=1”;
    // plus propre encore: lire les options de la fenêtre courante

    // ouvrir la fenêtre
    var newWindow = window.open (href,title,options);

    Like

  9. vinch's avatar Vinch says:

    Oui, je suis au courant. Merci 🙂

    Like

  10. Très rapidement:

    * si vous avez besoin de l’attribut target, utilisez le doctype adéquat, ça sert à ça
    * à ce sujet, target est invalide en XHTML1.0 Strict mais valide en Transitional et HTML5
    * forcer l’ouverture d’une nouvelle fenêtre est (presque) toujours une mauvaise idée
    * si vous choisissez de tout de même le faire, il est indispensable d’indiquer à l’utilisateur le comportement qui l’attend (WCAG)

    Like

  11. Oncle Tom's avatar Oncle Tom says:

    Pour ce genre de cas, il est même encore mieux d’utiliser la fonction live() de jQuery puisqu’il n’y aura qu’un seul évènement d’assigné.
    En cas d’injection de contenu en AJAX, ça fonctionnera aussi.

    Sinon, comme le signale Benjamin, dans le code indiqué, ajouter un rel=”external” avec le style CSS qui va bien pour indiquer à l’utilisateur qu’une nouvelle fenêtre s’ouvrira est une bonne idée.

    Like

  12. vinch's avatar Vinch says:

    @Benjamin : Choisir le bon doctype à la base est effectivement la meilleure idée mais si le site existe déjà, c’est très délicat de changer de doctype en cours de route ! Presque toujours une mauvaise idée, je ne sais pas. Quand bien même ça le serait, c’est devenu une pratique courante et je me vois mal me battre contre un client lambda qui souhaite cela en lui ressortant des arguments comme “W3C”, “WCAG” ou encore “Jakob Nielsen”…

    Like

  13. Bartdude's avatar Bartdude says:

    Comme le dit Benjamin D.C., il me semblait aussi avoir lu que le target faisait son retour en grâce avec le HTML5 (mais bon c’est pas pour demain qu’un IE le supporte…), et c’est pas plus mal. Ca me semble normal de pouvoir forcer ce comportement…

    Par contre ce recours au jquery pour tout et n’importe quoi a un peu tendance à m’énerver. Déjà qu’un $(‘a’) cache (sauf erreur) un paquet d’instructions, en rajoutant encore des instructions derrière ca fait encore plus de ressource client utilisée… Donc a moins d’avoir un besoin impérieux de forcer cette ouverture dans une nouvelle fenêtre pour un site déjà complet où ca n’est pas le cas, ca me semble un peu overkill…

    Like

  14. Marin's avatar Marin says:

    les window.open, bof je suis pas chaud. Mais bon j’imagine que c’est une contrainte client. Perso si je suis obligé de le faire, je rajoute une variable pour n’ouvrir qu’une seule fenêtre (et pas 500). Je vérifie que cette fenêtre n’est pas fermée, et je fais un focus sur celle-ci. Et puis j’indique avec un peu de css que je vais ouvrir une nouvelle fenêtre (a la wikipedia)

    pour ce qui est du jquery, je serai beaucoup plus spécifique, si tu veux vraiment l’employer, emploie le à bon escient:
    $(“.content a[href^=’http’]”) <- qui ne cible que les <a> dont l’attribut href commence par http et dans un element avec une class .content. Ca aura l’avantage de moins parser ton DOM et eviter des comparaisons.

    Et puis je préfère le bind au click, nah!

    Like

  15. Marin's avatar Marin says:

    les <a> je disais 🙂

    Like

  16. vinch's avatar Vinch says:

    @Marin : le truc du “une seule fenêtre”, ça m’a déjà cause préjudice (même si c’est un bien grand mot). Si tu cliques sur plein de liens en pensant les ouvrir à chaque fois dans un nouvel onglet et que tu te rends compte au final que juste le dernier a été pris en compte, c’est un peu chiant. D’accord avec toi pour moins parser le document, j’y avais pas pensé.

    Like

  17. Ced's avatar Ced says:

    En effet, j’avais déjà utilisé cette technique sur une page comportant beaucoup de liens, le navigateur mettait quasi une seconde en plus pour faire le rendu de la page. Ce serait peut-être plus efficace de rechercher les http/https coté serveur et leur ajouter une classe css spécifique.

    Like

  18. djdmsr's avatar djdmsr says:

    Tester l’existence de http ou https c’est oublier que les liens internes peuvent être écrits aussi en absolu! On peut demander au js d’extraire le nom du serveur un peu comme en php (window.location.hostname) et tester plutôt si l’url comporte le nom du serveur.

    Like

  19. vinch's avatar Vinch says:

    @djdmsr : si tu lis bien le script, tu comprendras que je gère ce cas !
    Dans le premier test, je regarde si le lien commence par http ou https.
    Si non, on ne va pas plus loin, le lien sera ouvert dans la même fenêtre.

    Like

  20. djdmsr's avatar djdmsr says:

    Je me suis mal expliqué! Et si ton lien interne est en absolu lui aussi (commençant avec http) comme un lien externe…

    Et puis autre chose (sans ironie, ni polémique), tu dis “J’ai toujours trouvé qu’il était du ressort de l’utilisateur de choisir s’il voulait (ou pas) ouvrir un lien dans une nouvelle fenêtre (ou un nouvel onglet).” Cette méthode ne donne pas plus le choix au visiteur, c’est en fait celui du webmaster: lien interne=même fenêtre; lien externe=nouvelle fenêtre.
    J’ai déjà réfléchi à cette problèmatique (du choix, pas de celle de l’alternative à _blank qui est autre chose). En fait la seule chose pertinente qu’on peut faire si on veut vraiment donner le choix à l’utilisateur (le Neuneu celui qui ne sait pas qu’il y a le clic droit) c’est de générer en js un second lien juste après le premier sur une icone qui lui ouvrira une nouvelle fenêtre. Mais cette méthode je ne l’ai jamais rencontrée. L’approche de wikipedia est différente: l’icône signale qu’on va changer de site, mais dans la même fenîetre.
    Sinon pour ce qui est de_blank je pense que les gars du w3c ne sont pas des couillons et qu’ils ont bien fait de le supprimer. Souviens toi du temps où il n’y avait pas d’onglet et que _blank était la règle, résultat: des fenêtres partout, qu’est-ce que c’était pénible!
    Si il y a des neuneus qui ne savent pas faire, et bien ils apprendront le click droit un jour en regardant les autres faire… je m’en tape! je préfère me soucier d’accessibilité (qui justifie la suppression de _blank), c’est bien plus louable et justifié.

    Like

  21. Vinch's avatar Vinch says:

    @djdmsr : En fait, je gère le cas aussi si le lien commence par http ! Relis bien 😉
    Pour le reste, je suis totalement d’accord avec toi !

    Like

  22. djdmsr's avatar djdmsr says:

    Autant pour moi! [if (host != window.location.host)]
    si on est ok c cool lol

    Like

  23. Gregoire's avatar Gregoire says:

    @djdmsr : j’ai pas compris le coup du clic droit (je fais peut-être partie des neuneus). Le fait de faire un clic droit sur un lien ne me dit pas si celui-ci va s’ouvrir dans un nouvel onglet ou pas. Ca permet de forcer éventuellement l’ouverture dans un nouvel onglet ou une nouvelle fenêtre, c’est tout. Si le lien a un target blank, et que je veux forcer l’ouverture dans le même onglet, je ne peux pas ; enfin pas facilement (il faut copier l’adresse du lien et la coller dans la barre d’adresse, youhou…)

    Sinon pour ma part j’ai installé le module “link alert” pour firefox, et il me prévient du type du lien que je survole, je trouve ça fort pratique 🙂

    Like

  24. Bartdude's avatar Bartdude says:

    > Si le lien a un target blank, et que je veux forcer l’ouverture dans le même onglet, je ne peux pas ; enfin pas facilement (il faut copier l’adresse du lien et la coller dans la barre d’adresse, youhou…)

    Oui ‘fin bon au pire ca t’oblige à fermer la fenêtre en cours après, c’est pas non plus un drame et ca ira de toute facon plus vite qu’une autre manipulation…

    Like

  25. Bartdude's avatar Bartdude says:

    >Sinon pour ce qui est de_blank je pense que les gars du w3c ne sont pas des couillons et qu’ils ont bien fait de le supprimer.

    Bah ptêtre un peu quand-même, et ils s’en rendent compte, vu qu’il est à nouveau au goût du jour en HTML 5

    Like

  26. K-feine's avatar K-feine says:

    djdmsr:
    “Si il y a des neuneus qui ne savent pas faire, et bien ils apprendront le click droit un jour en regardant les autres faire… je m’en tape! je préfère me soucier d’accessibilité (qui justifie la suppression de _blank), c’est bien plus louable et justifié.”

    Pour ce qui est louable et justifie, tout est une question de point de vue. Il me semble que ce qui est le plus jusitife est d’apporter une navigation coherente et intuitive a l’utilisateur, je ne vois pas trop l’interet de se soucier de l’accessibilite et tutti quanti si c’est pour se foutre de l’utilisateur lambda (qui sans etre neophyte n’est pas forcement a meme de savoir toute les differentes commandes offertes par son navigateur, ca ne fait pas de lui un “neuneu”).

    En tant qu’utilisatrice, il me semble bien plus logique de mettre les liens externes en ouverture de nouvelle fenetre par default car a l’utilisation il est plus frequent d’ouvrir des liens externes qui se trouvent au milieu du contenu d’une page en attente de la fin de lecture de ce contenu – comme dans un article de blog par ex.).
    D’ailleurs, meme pour celui qui connait le coup du clic droit pour afficher la possibilite d’ouvrir dans une nouvelle fenetre et la selectionnee. djdmsr, si tu l’a deja fait sur une page ou tu veux ouvrir plus de 3 liens, tu sauras que ca a vite tendance a te gonfler.
    Reste la solution du raccourci souris, (clic sur molette ou cmd+clic gauche).

    – D’ailleurs je profite de la parenthese pour demander si certains ont remarque cette tendance dans les blogs et les journaux a parler d’un produit ou un site, mettre le nom et/ou l’url et ne pas le linker ?? –

    En tout cas merci Vinch, c’est exactement la finition que je voulais developper et ajouter a mon projet ce matin. je n’aurais qu’a recopier ton code demain matin du coup :).

    Like

  27. K-feine's avatar K-feine says:

    *se rend compte qu’elle commente apres le deluge*

    Like

  28. Fredj's avatar Fredj says:

    Merci je viens d’apprendre encore une nouvelle chose grâce à ton blog.
    FredJ

    Like

  29. Thierry's avatar Thierry says:

    Merci à tous ceux qui, comme toi, distribue leur connaissances sur le net. Vivement que j’ai à mon tour le niveau de faire partager mes expériences et connaissances.
    Un grand merci à toi

    Like

  30. Kiscope Tv's avatar Kiscope Tv says:

    Comment ajouter a blogger

    Like

  31. Richnou's avatar Richnou says:

    Un peu du même avis que certain commentaires : un lien externe qui s’ouvre dans une nouvelle fenêtre ne me choque en rien, le comportement est suffisamment classique et largement utilisé pour en voir le retour en html5. Après tout, quand on lit un livre qui parle d’un sujet et renvoie à autre chose, on pose le livre pour prendre le dictionnaire… avant de refermer le dictionnaire et revenir au livre (ou pas).
    Se baser sur du jQuery pour ce faire est plutôt élégant, surtout si le site utilise déjà jQuery par ailleurs, ce qui est très fréquent.

    Dans la plupart de mes sites WordPress, j’opte souvent pour une solution légèrement différente, en l’occurrence, au chargement de la page, automatiquement rajouter le target à tous les liens qui ne pointent pas vers le domaine en cours (uniquement dans la div principale, le reste n’est que menus internes).
    Ce qui donnerait:
    $(‘#content a:not([href^=https://vinch.be/])’).attr( “target”, “_blank” );

    On retrouve l’avantage du target de base, sans passer par des windows.open (et on conserve la navigation par onglet quand elle existe).
    Bien sûr, cela suppose que tous les liens internes soient absolus (ce qui est le cas avec WordPress), et aucune injection de contenu ajax.

    Je complète avec un peu de CSS
    a[target=”_blank”] { padding-left:14px; background: url(/img/external.png) no-repeat; }
    Qui ajoute un picto (classique: carré avec une flêche) pour indiquer (ou évoquer) à l’utilisateur le comportement…

    Pour que ça marche en relatif, on doit pouvoir adapter grace a ton exemple pour ne l’ajouter qu’après analyse du window.location.hostname, genre $(‘a’).each(…) en remplaçant dans ton code la ligne window.open(href) par un $(this).attr( “target”, “_blank” ); je n’ai pas testé 🙂

    Ceci étant posé, je ne sais pas en terme de performance quelle solution est meilleure (ou moins pire selon les avis).
    (désolé pour le réchauffé, mais je découvre ce blog…)

    Like

  32. Un des moyens que l’on a aussi est d’afficher à côté du lien un petit icone qui indique à l’utilisateur que le lien s’affiche dans une nouvelle fenêtre…
    Le graphisme vient « au secours » de la philosophie quant à savoir si l’on doit ouvrir les liens dans une fenêtre nouvelle ou pas. Et le petit icone permet de varier suivant le lien.

    Like

  33. cedcyr's avatar cedcyr says:

    bonjour, pour info a la base je suis mécano et code que de temps en temps et donc le neuneu c’est surement moi ! me voila donc en train de crée un nouveau menu pour rafraichir mon site et je cherche pour que certain lien s’ouvre dans une nouvelle fenêtre ou onglet (cela dépend des paramétrage du navigateur) et je tombe donc sur cette discutions ! j’ai donc voulu essayé, j’ai pris ce bout de code $(‘a[rel=”external”]’).click(function() { window.open($(this).attr(‘href’)); return false;}); que j’ai positionné dans une feuille script et que j’appel comme il se doit par un rel=”external”] et quand je test ma page le débogueur se met en route et me dit (SCRIPT5009: « $ » est indéfini ) pourquoi est ce que avant $ je mette autre chose merci d’avance car dans mes manuel surment un peut encien je ne trouve pas

    Like

  34. Joleen's avatar Joleen says:

    Way cool! Some extremely valid points! I appreciate you penning this article and the rest of the site is really good.

    Like

Leave a reply to MrThieu Cancel reply