Étendre les liens en JavaScript (ou en CSS)

L’astuce d’aujourd’hui est un bout de code que j’utilise sur quasiment tous les sites que je réalise. Elle me sert à concilier une bonne pratique de référencement et une bonne pratique d’ergonomie qui restaient jusqu’ici incompatibles.

Dans l’univers du référencement, l’ancre d’un lien doit idéalement être un texte de quelques mots vecteurs de sens. Ce lien doit également être unique au sein du document, pour ne pas diluer le jus de référencement transféré vers la page cible. Par exemple : dans une liste d’article de blog on mettra le lien vers la page sur le titre.

Dans l’univers de l’ergonomie c’est un peu différent. Toujours dans cette liste d’article : l’internaute sait qu’un clic sur le titre l’emmènera sur la page de détail, mais il s’attend aussi à pouvoir cliquer sur l’image de miniature ou sur le texte « lire la suite ». En fait au niveau de la navigation le mieux c’est quand un bloc est cliquable intégralement.

Ces deux approches semblent opposées. Cependant l’optimisation des liens pour le SEO ne concerne que le code source de la page, celui qui sera analysé par les moteurs. L’approche ergonomique s’adresse au DOM ; la page calculée par le navigateur.
Entre les deux il y a – souvent – JavaScript !

Edit : en re-effectuant un test ce matin, après avec discussion avec des confrères, je me rend compte que cette solution n’est pas optimale. Je la laisse pour la postérité et vous invite à utiliser l’astuce uniquement CSS détaillée plus bas dans l’article.

La solution que je propose, c’est de placer les liens sur les textes idéals pour le référencement, puis au chargement de la page, les déplacer sur des éléments parents. Ainsi on conserve le meilleur des deux mondes.

étendre lien JavaScript
Un lien sur le titre, à déplacer sur le contenant du bloc

Quel balisage mettre en place pour utiliser cette astuce ?

Afin d’utiliser le petit code JavaScript que je vais vous donner, il est nécessaire d’adapter le markup (le balisage de votre document). Sur les balises <a/> que nous voudrons étendre, nous allons ajouter un attribut data-expand-link, et sur les éléments parents que nous souhaiterons transformer en lien nous ajouterons l’attribut data-expand-target.

<!-- le futur lien ↴ -->
<div class="foo" data-expand-target>
	<h2>
		<!-- le lien ↴ -->
		<a href="#bar" target="_blank" data-expand-link>mon lien</a>
	</h2>
	<p>Un petit texte</p>
</div>
le balisage de base

Par défaut, il faut aussi choisir ce que deviendra le lien d’origine… le parent deviendra forcément un <a/>, mais en fonction de l’ergonomie ou des éléments contenants, on peut souhaiter que l’ancien lien devienne un <span/>, une <div/> ou un <button/> par exemple…

Je propose que le lien devienne une balise <div/> par défaut, mais qu’en ajoutant un attribut data-expand-type="span" on puisse définir un comportement différent.

<div class="wrapper" data-expand-target>
	<span>
		<!-- si le lien ci-dessous devient une <div/>,
		… le span contenant ne va pas aimer… -->
		<a href="/foo" data-expand-type="span" data-expand-link>lien</a>
	</span>
</div>
avec l’attribut data-expand-type

Le code de transformation des balises

Pour mettre en place notre astuce, il nous faut créer un fichier JavaScript que nous chargerons par le biais d’un traditionnel wp_enqueue_script (dans votre thème ou dans une extension) :

add_action( 'wp_enqueue_scripts', 'enqueue_link_expand_script' );
function enqueue_link_expand_script() {
	wp_register_script( 'link-expand', 
		get_template_directory_uri() . '/library/link-expand.js', 
		array( 'jquery' ) 
	);
	wp_enqueue_script( 'link-expand' );
}
Charger un script dans le thème

Dans ce fichier, on s’assure que le document est complètement chargé et que jQuery est bien présent. Ensuite nous allons écrire les deux fonctions nécessaires :

La première, ligne 2, nous permet de transformer la nature d’une balise, sans modifier ses attributs ni ce qu’elle contient.

La fonction suivante, nommée applyExpandLink() ligne 16, parcours le contenu à la recherche des liens à étendre. Pour chacun d’eux elle localise le parent qui deviendra le lien, elle lui transfert ses attributs puis change la nature des deux balises.

jQuery(document).ready(function($){
	function changeType( $elem, tag ) {
		if ( tag == $elem.get(0).tagname ) {
			return false;
		}

		var $newElem = $( '<' + tag + '>' );
		attributes = $elem.prop( 'attributes' );
		$.each(attributes, function() {
			$newElem.attr( this.name, this.value );
		});

		$newElem.append( $elem.html() );
		$elem.replaceWith( $newElem );
	}
	function applyExpandLink() {
		var $parent, attributes, type;
		$( 'a[data-expand-link]' ).each(function(){
			$parent = $(this).parents( '[data-expand-target]' );
			attributes = {
				href: $(this).attr( 'href' ),
				hreflang: $(this).prop( 'hreflang' ),
				target: $(this).prop( 'target' ),
				title: $(this).prop( 'title' ),
				rel: $(this).prop( 'rel' ),
			};
			type = $(this).attr( 'data-expand-type' );
			if ( 'undefined' == type || ! type ) {
				type = 'div';
			}
			$(this).removeAttr( 'data-expand-link \
				data-expand-type target \
				title rel href hreflang' );
			changeType( $(this), type );

			$parent.attr( attributes ).removeAttr( 'data-expand-target' );
			changeType( $parent, 'a' );
		});
	}
	applyExpandLink();
});

Si vous utiliser ce script sur un site qui charge des contenus dynamiquement, vous pourrez re-exécuter  applyExpandLink() pour forcer la fonction à reparcourir le document et exécuter les transformations sur les liens fraichement insérés.

Une dernière précision pour finir : le seul élément que ne peut pas contenir un lien, c’est un autre lien ! Soyez donc vigilant à ne pas mettre plusieurs <a/> dans une même balise ayant l’attribut data-expand-target. Dans les faits cela ne changerait rien (c’est la magie des spécifications HTML5 qui prévoient les comportements des navigateurs en cas d’erreur ????) mais votre document calculé ne serait plus valide.

Edit : en CSS c’est mieux

Comme je le disait en introduction, c’est une astuce que j’utilise depuis de nombreuses années. À l’époque j’avais fait un test pour être sur que Google n’interprétait pas la modification du DOM en javascript. Suite à la publication de cet article et une discussion sur le slack j’ai décidé de refaire la vérification. Les résultat est vite tombé : Google interprète ce genre de manipulation légère, et cette pirouette en JavaScript n’est donc plus la bonne solution (par contre Google ne donne pas suite aux requête ajax, ce qui m’avais conforté dans l’erreur lors de mes tests plus récents).

Pour rendre l’ensemble du lien d’un bloc cliquable, il reste une solution pure CSS dont les bases sont issues de cet article d’HTeuMeuLeu et que j’ai légèrement modifié.

L’idée c’est toujours d’avoir un lien, et un élément contenant (gardons leurs les attributs data-expand-target sur le parent et data-expand-link sur le lien, comme pour la méthode JS), mais cette fois-ci nous allons utiliser un pseudo élément du lien pour le faire déborder, en CSS du lien. Il suffit donc de mettre ce code CSS dans votre feuille de style :

[data-expand-target]{
	overflow:hidden;
	position:relative;
}

[data-expand-link]:after{
	content:'';
	position:absolute;
	top:0;
	left:0;
	right:0;
	bottom:0;
	z-index:10;
	margin:-250px;
}
Étendre un lien en CSS

Par rapport à l’article d’HTeuMeuLeu j’ai juste ajouté un overflow:hidden sur l’élément conteneur et une marge négatif sur le pseudo-élément. En effet, si le lien est positionné autrement qu’en static le pseudo-élément serait bloqué aux frontière du lien, le margin:-250px (il est possible de mettre plus si besoin) permet de le propager au-delà, et l’overflow de cacher ce qui dépasse de l’élément parent.Voilà…

6 commentaires
Gaël Poupard, il y a 12 mois
Idée intéressante, merci pour le partage !

En partant du même constat et avec un éclairage accessibilité plutôt que SEO / ergo, je suis arrivé à une solution différente : mettre plusieurs vers la même cible (titre, « lire la suite », miniature) ainsi ils sont tous utilisables, cependant j’en sors deux de l’index de tabulation et de la liste des liens — tel qu’exposée par un lecteur d’écran — en leur conférant un aria-hidden="true" et un tabindex="-1".

Ainsi le lien vers l’article n’apparaît qu’une fois dans la liste de liens exposée, et en naviguant on ne franchit qu’un seul lien au lieu de trois (ce qui se traduit par un confort et une navigation plus fluide au clavier).

L’idée a été proposée par Johan Ramon, dans cette présentation : http://www.johanramon.fr/accessibiliser-subtilite/ (allez au slide 13, mais lisez aussi le reste, hein :p).

Je n’ai cependant pas réfléchi aux impacts SEO, je suppose malheureusement que les liens sont suivis et donc « diluent le jus », pour parler le jargon.

Willy Bahuaud, il y a 12 mois
Merci pour le partage !
Effectivement cela fonctionne parfaitement en terme d’accessibilité 🙂

Thomas Villain m’a aussi parlé d’une autre astuce qui consiste à jouer du css pour faire dépasser un pseudo élément d’une balise <a/> pour l’étendre aux limites de son parent. Par contre du coup ça veut dire que l’on ne peut pas utiliser de règle de positionnement css à l’intérieur du parent.

Ludovic RIAUDEL, il y a 12 mois
Bonjour Willy,

Merci pour ce partage.
As tu des exemple concret de cette mise en place ?
Merci

Willy Bahuaud, il y a 12 mois
Wabeo (ce site) en est un. Sur la page d’accueil les liens sont sur les <h2> et repositionnés sur le bloc 🙂
Matthieu Scarset, il y a 12 mois
Salut Willy,

Comme tu le commentes un peu plus haut, moi je suis partisan d’un :before en position: absolute qui occupe toute la widht et la height de son container.

C’est facilement faconnable pour etre rendu accessible et le CSS est super simple du coup.

Une petite mixin SASS et le tour est joué! 🙂

Willy Bahuaud, il y a 12 mois
Oui, c’est ce que je fais maintenant. La technique à cependant quelques défauts à nuancer :

– Si le lien est positionné, il faut alors jouer avec la marge négative pour aller jusqu’aux bords du conteneur (cela dépend des cas) ;
– s’il y a un élément en overflow:hidden entre le lien et son wrapper, l’astuce ne fonctionnera pas (c’est par exemple le cas si on souhaite faire un contexte de formatage de blocs) ;
– Les effets :focus sur le lien ne pourront pas remonter sur le conteneur :-/ ;
– On est obligé de se « sacrifier » un pseudo-élément qui peut servir au design.

Après, ces défauts sont évidemment à nuancer 🙂

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Publié le 26 octobre 2016
par Willy Bahuaud
Catégorie Intégration web, Référencement naturel