Accessibilité VS accordéons

L'accessibilité sur le Web n'est pas toujours facile lorsqu'on veut utiliser certains type d'affichage tels que les accordéons ou les menus déroulants. JQuery offre des fonctions rapides comme slideToggle(), mais il faut les utiliser avec prudence.

Ces fonctions marchent très bien. Le problème est qu'on ne peut les utiliser si on souhaite rendre accessible aux lecteurs vocal l'information cachée, car les différentes fonctions de slide se terminent tous par la propriété display:none à l'état fermé. Et nous savons que les lecteurs vocal ignorent tout ce qui est caché avec display:none;

Heureusement, on peut tout de même utiliser cette présentation et cette animation tout en rendant le contenu accessible avec un peu d'imagination.

Le principe général est d'utiliser la classe "visuallyhidden" (provenant d'HTML5 Boilerplate) après l'utilisation de la fonction slideUp.

Le code

Prenons le code HTML suivant :

<div class="accordeon">
<h2 class="toggler">Exemple de titre cliquable</h2>
<div>
<p>Le contenu texte de l'accordéon</p>
</div>

<h2 class="toggler">Second titre cliquable</h2>
<div>
<p>Autre texte d'accordéon</p>
</div>
</div>

Voici d'abord la façon normale/courte (non accessible) d'utiliser les accordéons :

// Cacher les blocs de contenu d'accordéon au chargement
$(".accordeon > div").hide();

// Les boutons d'action pour ouvrir/fermer les accordéons
var togglers = $('.accordeon > .toggler');

togglers.click(function(){
	// Switch au click
	$(this).toggleClass("ouvert").next().slideToggle("fast", 'linear', function(){  });
	return false; // Prévient le navigateur d'activer la cible du lien
});

Voici maintenant le même comportement, mais en laissant accessible le contenu à l'état fermé grâce à la classe "visuallyhidden".

// Cacher les blocs de contenu d'accordéon au chargement (mais laisser accessible)
$(".accordeon > div").addClass('visuallyhidden');

var togglers = $('.accordeon > .toggler');

// Switch au click
togglers.click(function(){
	var me = $(this);
	var div = me.next();
	if (me.hasClass("ouvert")) {
		// Fermer, mais laisser accessible
		me.removeClass("ouvert");
		div.slideUp("fast", 'linear', function(){
			$(this).addClass("visuallyhidden").show();
		});
	}
	else {
		// Ouvrir
		me.addClass("ouvert");
		div.hide().removeClass("visuallyhidden").slideDown("fast", 'linear', function(){  });
	}
	return false; // Prévient le navigateur d'activer la cible du lien
});

On peut voir qu'il suffit d'appeller la fonction show() pour forcer la propriété display:block après avoir ajouté la classe "visuallyhidden" à la fin de la fermeture de l'accordéon (dans le callback). À l'inverse, il faut appeler la fonction hide() d'abord avant d'enlever la classe "visuallyhidden", puis appeller la fonction slideDown() pour ouvrir l'accordéon.

Mise à jour 30-11-2013.

Jusqu'ici, l'accessibilité proposée n'était que pour les lecteurs vocaux, mais il faut aussi penser à la navigation clavier! Voici un ajout pour faciliter la navigation au clavier (par tabulation). En bonus, j'inclus les attributs WAI-ARIA

// Cacher le bloc de contenu de l'accordéon au chargement
$(".accordeon").attr('aria-multiselectable', 'true'); // ARIA
$(".accordeon > div").addClass('visuallyhidden');

// Désactiver les liens des accordéons pour le TAB
$(".accordeon > div a").attr('tabindex', '-1');

var togglers = $('.accordeon > .toggler');

// Switch de l'ouverture à la fermeture au click
togglers.on('click keypress', function(e){
	// Exécute seulement si cliqué ou keypress avec la touche ENTER (#13)
	if(!e) {e=window.event;}
	var keynum = e.keyCode||e.which;
	if (e.type == 'keypress' && keynum != 13) {
		return true;
	}
	
	var me = $(this);
	var div = me.next();
	
	if (me.hasClass("ouvert")) {
		// Fermeture, mais laisser accessible
		me.removeClass("ouvert");
		
		div.find('a').attr('tabindex', '-1'); // Désactiver le TAB sur les liens
		div.attr('aria-hidden', 'true').slideUp('fast', 'linear', function(){
			$(this).addClass("visuallyhidden").show(); // Laisser accessible
		});
	}
	else {
		// Ouverture
		me.addClass("ouvert");
		
		div.find('a').attr('tabindex', '0'); // Permet le TAB sur les liens
		div.hide().removeClass("visuallyhidden").attr('aria-hidden', 'false').slideDown('fast', 'linear', function(){ });
	}
	return false; // Prévient le navigateur d'activer la cible du lien
});

// Initialise l'état de tous les accordéons (ARIA)
togglers.each(function(index, element) {
	var me = $(this);
	var div = me.next();
	
	// ARIA
	var id = div.attr("id");
	if (id == undefined) {
		id = 'AccPanel'+'-'+index;
		div.attr('id', id);
	}
	
	me.attr('tabindex', '0'); // Permet le focus sur le toggler (si pas un <a>)
	me.attr('aria-controls', id);
	me.attr('role', 'tab');
	div.attr('aria-hidden', 'true');
	div.attr('role', 'tabpanel');
});

Pour la navigation clavier, il faut "désactiver", à la tabulation, tous les liens lorsque l'accordéon est fermé. On y arrive à l'aide de l'attribut tabindex="-1". Notez que tout autre balise ou champ de formulaire pouvant obtenir le focus devrait être désactiver aussi à l'intérieur des accordéons fermés.

Voici le code CSS provenant de HTML5 Boilerplate :

/* Hide only visually, but have it available for screenreaders: h5bp.com/v */
.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }

/* Extends the .visuallyhidden class to allow the element to be focusable when navigated to via the keyboard: h5bp.com/p */
.visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; }