Tooltip accessible et SEO
Objectif d'un « tooltip » accessible
L'idée de départ est de pouvoir concilier accessibilité, design et référencement avec les fameuses « infobulles » surgissantes sur les pages web.
Habituellement, ce type de présentation fonctionne sur les événements mouseover et mouseout,
ce qui rend l'effet inopérant en navigation au clavier. De plus, les lecteurs d'écran n'interagissent pas (ou mal)
avec ce type d'objet.
Bien entendu, la bonne pratique est d'alimenter le code avec l'attribut title…
Le fonctionnement de ce tooltip tente donc de concilier toutes ces petites choses.
Le markup
<button class='cta cta-secondary cta-small vertical-align-middle' data-tooltip title="Lorem ipsum dolore ">Passer la souris ici</button>
Ce qui produit ceci :
Toute proposition d'amélioration est bien sûr la bienvenue !
Ça se passe ici : contact
jQuery.fn.tooltip
/*
 * Copyright © 2006 2024. Kortic (URL : https://www.kortic.com/info-bulle-html-accessible.html)
 * licence Creative Commons CC BY-NC-SA 4.0
 * https://creativecommons.org/licenses/by-nc-sa/4.0/deed.fr
 */
 
;( (__) => {
 'use strict';
 __.focusable = function( tag, element_ ) {
 let is_focusable = false;
 if ( /^(input|select|textarea|button|object)$/.test( tag ) ) {
 is_focusable = !element_.disabled;
 } else if ( 'a' === tag ) {
 is_focusable = element_.attr('href') !== undefined;
 }
 if(element_.attr('contenteditable') !== undefined) {
 is_focusable = __.attr(element_, 'contenteditable') !== false;
 }
 return is_focusable && element_.is( ":visible" );
 };
 __.fn.tooltip = function(o) {
 let settings = __.extend({
 'attribute': 'data-tooltip',
 'css': null,
 'live' : 'polite'
 }, o),
 setDelay = 1000;
 let tooltip = DOM.body.find('#tooltip');
 if(!tooltip.length) {
 tooltip = __('<div/>', {
 'id': 'tooltip',
 'role': 'tooltip',
 'aria-live': settings.live,
 'aria-hidden': 'true'
 });
 DOM.body.append(tooltip);
 } else {
 tooltip.attr('aria-live', settings.live);
 }
 __(this).each(function() {
 let element_ = __(this),
 tooltipContent = element_.attr(settings.attribute) || '';
 if(!tooltipContent.length) {
 if(element_.attr('title') !== undefined) {
 element_.data({
 'content': element_.attr('title'),
 'type': 'title',
 'css': settings.css
 });
 } else {
 element_.data({
 'content': null,
 'type': null
 });
 }
 } else {
 element_.data({
 'content': tooltipContent,
 'type': settings.attribute,
 'css': settings.css
 });
 }
 if(element_.data('content') != null) {
 (element_.attr('tabindex') === undefined && !__.focusable(element_.tagName(), element_)) && element_.attr({
 'tabindex': '0'
 });
 if(settings.live !== 'off') {
 element_.attr({
 'aria-describedby': 'tooltip'
 })
 }
 element_.on({
 'mouseover focus': (event_1) => {
 let eventType = event_1.type;
 tooltip.removeAttr('style, css').html(element_.data('content'));
 (element_.data('css') != null && tooltip.addClass(element_.data('css')));
 if (eventType === 'mouseover') {
 element_.on('click', () => {
 element_.off('focus');
 });
 event_1.stopPropagation();
 }
 element_.data('type') === 'title' ? element_.data('title', element_.data('content')) : element_.data('title', null);
 element_.removeAttr('title');
 let elementHeight = element_.outerHeight() === 0 ? this.getBoundingClientRect()['height'] : element_.outerHeight(),
 elementWidth = element_.outerWidth() === 0 ? this.getBoundingClientRect()['width'] : element_.outerWidth();
 let x = (element_.offset().left + (elementWidth / 2)).toFixed(0),
 y = element_.offset().top + elementHeight,
 HTMLOuterWidth = DOM.html.outerWidth(true) / 2;
 element_.data('class', element_.offset().left > HTMLOuterWidth ? 'right' : 'left');
 tooltip.delay(setDelay).queue( () => {
 let scrolltop = DOM.document.scrollTop(),
 w = tooltip.outerWidth(true),
 h = tooltip.outerHeight(true),
 x_limit = (DOM.window.outerWidth() - w) + parseInt(DOM.html.css('padding-left'), 10) + parseInt(DOM.html.css('padding-right'), 10),
 reversing = (y + h - scrolltop + 25) > DOM.window.height();
 reversing ? tooltip.addClass('reverse') : tooltip.removeClass('reverse');
 x -= (w / 2);
 y += 10;
 (x < 15) && (x = 15);
 (x > x_limit) && (x = x_limit - 30);
 reversing && (y -= elementHeight + h + 20);
 tooltip
 .attr({'aria-hidden': 'false'})
 .css({
 'z-index': __.zindex(),
 'left': x + 'px',
 'top': (y - scrolltop) + 'px',
 'visibility': 'visible'
 })
 .dequeue();
 DOM.document.on('keyup', e => {
 (e.which || e.keyCode) === KEYCODES.ESC && (
 tooltip.dequeue().css({
 'visibility': 'hidden'
 }).empty().attr({
 'aria-hidden': 'true'
 })
 );
 });
 });
 },
 'mouseout blur click': () => {
 (element_.data('title') != null && element_.attr({
 'title': element_.data('title')
 })
 );
 tooltip.dequeue().css({
 'visibility': 'hidden'
 }).empty().attr({
 'aria-hidden': 'true'
 });
 }
 });
 }
 return this;
 });
 };
})(jQuery);
Utilisation
// Appel sur un $(document).ready()
// placer un tooltip sur toutes les balises `abbr`
 $('abbr').attr('data-tooltip','');
// placer un tooltip sur des balises spécifiques
 $('[data-tooltip]').tooltip();
Nécessite la fonction jQuery.zindex()
$.zindex = function (fromDomNode) {
 var r = [];
 $("*", (fromDomNode == null ? $('body') : fromDomNode )).each(function () {
 var z = parseInt($(this).css('z-index'), 10);
 !isNaN(z) && r.push(z)
 });
 var last = r.sort(function (a, b) {
 return a-b
 }).reverse()[0];
 if(last == undefined) {
 last = 0;
 }
 return last + 1;
};
Source CSS
/*
 * Copyright © 2006 2024. Kortic (URL : https://www.kortic.com/info-bulle-html-accessible.html)
 * licence Creative Commons CC BY-NC-SA 4.0
 * https://creativecommons.org/licenses/by-nc-sa/4.0/deed.fr
 */
 
#tooltip {
 background-color: black;
 border-radius: var(--radius);
 color: white;
 font-family: var(--base-font);
 font-size: 1rem;
 line-height: 1.25rem;
 max-width: 25vw;
 padding: 0.3125rem 0.625rem;
 position: fixed;
 text-align: left;
 visibility: hidden;
}
@media (prefers-color-scheme: dark) {
 #tooltip {
 background-color: white;
 color: black;
 }
}
#tooltip:empty {
 display: none;
}
Vous aimez cette page ?
Dites-le !
Utilisation des sources
Les sources proposées sur Kortic Anthony Ladeuil sont mises à disposition selon les termes suivants :
Licence Creative commons CC BY-NC-SA 4.0
- Attribution
-
Détails
Vous devez créditer l'Œuvre, intégrer un lien vers la licence et indiquer si des modifications ont été effectuées à l'Oeuvre. Vous devez indiquer ces informations par tous les moyens raisonnables, sans toutefois suggérer que l'Offrant vous soutient ou soutient la façon dont vous avez utilisé son Oeuvre.
- Pas d’utilisation commerciale
-
Détails
Vous n'êtes pas autorisé à faire un usage commercial de cette Oeuvre, tout ou partie du matériel la composant.
Bien entendu, la probabilité est faible pour que ce point soit respecté. En cas de sursaut d'honnêteté, vous pouvez payer un café, cette page soulagera votre conscience. - Partage dans les mêmes conditions
-
Détails
Dans le cas où vous effectuez un remix, que vous transformez, ou créez à partir du matériel composant l'Oeuvre originale, vous devez diffuser l'Oeuvre modifiée dans les même conditions, c'est à dire avec la même licence avec laquelle l'Oeuvre originale a été diffusée.
À défaut de tout ça, un petit message et une référence à ce site restent les bienvenus.
Limitation de responsabilité
Le code est fourni « tel quel », sans garantie d'aucune sorte, expresse ou implicite. En aucun cas l’auteur ou les détenteurs du copyright ne seront responsables de toute réclamation, dommage ou autre responsabilité, que ce soit dans une action contractuelle, délictuelle ou autre, résultant de, en dehors ou en relation avec le logiciel ou l'utilisation ou d'autres transactions.