Composants d’interface

Développer des composants d’interface riche (onglets, accordéons, fenêtres modales,…) est probablement une des tâches les plus récurrentes en développement front. Si certaines sont assez triviales (les onglets par exemple), d’autres peuvent se révéler beaucoup plus exigeantes.

Cet état de fait a permis l’émergence de nombreuses extensions (plugins) et bibliothèques (libraries) diverses et variées pour nous faciliter la tâche.
Seulement voilà : ces extensions reposent le plus souvent sur une dépendance plus importante (comme jQuery) et ne proposent pas toujours un bon niveau d’accessibilité.

Partant de ce constat, nous allons aborder des solutions pour créer quelques composants d’interface sans dépendance extérieure et avec un bon niveau d’accessibilité.

La recommandation est de toujours préférer l’emploi de quelques composants spécialisés répondant à un besoin spécifique, plutôt qu’une grosse bibliothèque générique qui fait tout y compris ce dont on n’a pas besoin.

L’emploi du vanilla JS est l’objet de ce cours, il nous permettra de pouvoir prendre en charge les comportements des modules : interactions clavier, prise de focus, etc.

Composants étudiés

Onglets

La solution simple

Voici le résultat auquel on veut arriver :

Pour y parvenir, partez du code HTML suivant :

<div class="container--tabs">
  <section class="row">
    <ul class="nav nav-tabs">
      <li class="active"><a href="#tab-1">Onglet 1</a></li>
      <li><a href="#tab-2">Onglet 2</a></li>
      <li><a href="#tab-3">Onglet 3</a></li>
    </ul>
    <div class="tab-content">
      <div id="tab-1" class="tab-pane active"> 
        <span class="col-md-10">
          <h3>Contenu onglet 1</h3>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
        </span>
      </div> 
      <div id="tab-2" class="tab-pane">
        <span class="col-md-10">
          <h3>Contenu onglet 2</h3>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
        </span>
      </div>
      <div id="tab-3" class="tab-pane">
        <span class="col-md-10">
          <h3>Contenu onglet 3</h3>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
        </span>
      </div>
    </div>
  </section>
</div>

Vous pouvez utiliser les règles CSS suivantes :

.container--tabs .nav-tabs {
  margin: 0;
  list-style-type: none;
  border-bottom: 1px solid #ddd;
  overflow: hidden;
}
.container--tabs .nav-tabs > li {
  float: left;
  margin-bottom: -1px;
}
.container--tabs .nav-tabs > li > a {
  float: left;
  margin-right: .125em;
  line-height: 1.4;
  padding: .75em;
  border: 1px solid transparent;
  border-radius: 4px 4px 0 0;
}
.container--tabs .nav-tabs > li > a:hover,
.container--tabs .nav-tabs > li > a:focus {
  border-color: #eee #eee #ddd;
}
.container--tabs .nav-tabs > li.active > a,
.container--tabs .nav-tabs > li.active > a:hover,
.container--tabs .nav-tabs > li.active > a:focus {
  color: #555;
  border: 1px solid #ddd;
  border-bottom-color: transparent;
}
.container--tabs .tab-content {
  clear: left;
}
.container--tabs .tab-content > .tab-pane {
  display: none;
  padding: 2.5% 3.5%;
  background-color: #efefef;
}
.container--tabs .tab-content > .tab-pane.active {
  display: block;
}
.container--tabs .tab-content > .active {
  display: block;
}

Vous créerez une fonction JavaScript, qui sera appelée par un écouteur d’événement « click » que vous aurez mis sur les liens de navigation de vos onglets. Cette fonction aura pour rôle de :

  • masquer tous les panneaux,
  • montrer le panneau ciblé,
  • qualifier tous les liens de navigation des onglets comme inactif (en supprimant la classe css « active« ),
  • activer le lien de navigation juste cliqué (en ajoutant la classe css « active« ).

Et pour améliorer le comportement pour les utilisateurs dont le JavaScript est désactivé, ajoutez par défaut une classe « no-js » à l’élément body. Adaptez vos scripts et CSS pour que les panneaux ne soient pas masqués par défaut si le JavaScript est désactivé.

La vraie solution utilisable en production

Ses avantages

  • Basé sur le Design Pattern ARIA pour les onglets ;
  • Pas de CSS/JavaScript injecté en ligne, le DOM reste propre, ce qui cool pour le responsive ;
  • Vous pouvez le styler comme vous voulez ;
  • Vous pouvez utiliser des transitions… comme vous voulez ;
  • Vous pouvez créer un namespace pour les classes générées si vous avez besoin de différents comportements dans la même page ;
  • Chaque aspect du script peut être personnalisé, si vous transpilez le script en ES5, ce sera compatible IE9+. ;
  • Léger : 20kb (version lisible par les humains), 6kb (version minifié), 2kb (minifié et gzippé)
  • Libre et sous une license permissive : placé sous licence MIT, donc c’est libre, open-source et vous pouvez faire ce que vous voulez avec, incluant une utilisation commerciale. Cette note de permission doit être incluse dans toutes les copies complètes ou partielles du script.

Contrôle au clavier

Si le focus est sur les “boutons” (contrôles) des onglets :

  • utilisez Haut/Gauche pour voir l’onglet précédent,
  • utilisez Bas/Droite pour voir l’onglet suivant
  • utilisez Home pour voir le premier onglet (où qu’on soit sur les boutons des onglets)
  • utilisez End pour voir le dernier onglet (où qu’on soit sur les boutons des onglets)

Si le focus est dans les contenus des onglets :

  • utilisez Ctrl Haut/Gauche pour remettre le focus sur le bouton correspondant au contenu
  • utilisez Ctrl PageUp pour remettre le focus sur le bouton précédent celui qui correspond au contenu
  • utilisez Ctrl PageDown pour remettre le focus sur le bouton suivant celui qui correspond au contenu

Bonus

Onglet ouvert par défaut

Si vous souhaitez qu’un onglet soit ouvert par défaut, c’est possible en mettant data-selected="1" sur le .js-tablist__link que vous souhaitez sélectionner.

Ancre ajoutée à l’URL

Comme vous l’aurez remarqué (ou pas), le script ajoute un fragment à l’URL quand vous cliquez ou sélectionnez un onglet au clavier. C’est cool quand vous voulez copier/coller le lien de la page que vous êtes en train de lire, et l’onglet ouvert sera le bon !

Ancre dans un contenu d’onglet

Si vous avez besoin de faire un lien vers une page avec un id qui est dans un contenu d’onglet, le script le détectera et affichera le bon onglet, rien que pour vous.

Lien « dans une même page » vers un onglet

Si vous avez besoin de créer un lien dans la même page vers un onglet, c’est possible : ajoutez-y la classe class="js-link-to-tab", et c’est tout. Le script fera le boulot.

Sources

https://van11y.net/fr/onglets-accessibles/

La solution jQuery

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>jQuery UI Tabs - Default functionality</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.13.0/themes/base/jquery-ui.css">
  <link rel="stylesheet" href="/resources/demos/style.css">
  <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
  <script src="https://code.jquery.com/ui/1.13.0/jquery-ui.js"></script>
  <script>
  $( function() {
    $( "#tabs" ).tabs();
  } );
  </script>
</head>
<body>
 
<div id="tabs">
  <ul>
    <li><a href="#tabs-1">Nunc tincidunt</a></li>
    <li><a href="#tabs-2">Proin dolor</a></li>
    <li><a href="#tabs-3">Aenean lacinia</a></li>
  </ul>
  <div id="tabs-1">
    <p>Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.</p>
  </div>
  <div id="tabs-2">
    <p>Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis. Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.</p>
  </div>
  <div id="tabs-3">
    <p>Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.</p>
    <p>Duis cursus. Maecenas ligula eros, blandit nec, pharetra at, semper at, magna. Nullam ac lacus. Nulla facilisi. Praesent viverra justo vitae neque. Praesent blandit adipiscing velit. Suspendisse potenti. Donec mattis, pede vel pharetra blandit, magna ligula faucibus eros, id euismod lacus dolor eget odio. Nam scelerisque. Donec non libero sed nulla mattis commodo. Ut sagittis. Donec nisi lectus, feugiat porttitor, tempor ac, tempor vitae, pede. Aenean vehicula velit eu tellus interdum rutrum. Maecenas commodo. Pellentesque nec elit. Fusce in lacus. Vivamus a libero vitae lectus hendrerit hendrerit.</p>
  </div>
</div>
 
 
</body>
</html>

Accordéon

La solution simple

Voici le résultat auquel on veut arriver :

Pour y parvenir, partez du code HTML suivant :

<a href="#content-1" class="accordion-toggle">Voir plus 1</a>
<div class="accordion-content" id="content-1">
  Contenu du premier panneau...
</div>

<a href="#content-2" class="accordion-toggle">Voir plus 2</a>
<div class="accordion-content" id="content-2">
  Contenu du deuxième panneau...
</div>

<a href="#content-3" class="accordion-toggle">Voir plus 3</a>
<div class="accordion-content" id="content-3">
  Contenu du troisième panneau...
</div>

Vous pouvez utiliser les règles CSS suivantes :

.accordion-toggle {
  display: block;
}
.accordion-content {
  display: none;
}
.accordion-content.active {
  display: block;
}

Et pour améliorer le comportement pour les utilisateurs dont le JavaScript est désactivé, ajoutez par défaut une classe « no-js » à l’élément body. Adaptez vos scripts et CSS pour que les panneaux ne soient pas masqués par défaut si le JavaScript est désactivé.

La vraie solution utilisable en production

Bonus

Contenu ouvert par défaut

Si vous voulez qu’un contenu de l’accordéon soit ouvert par défaut, ajoutez l’attribut data-accordion-opened="true" sur un hx, exemple :

Accordéons imbriqués et sélecteurs plus cools
Plusieurs panels ouvert en même temps

Le design pattern ARIA pour les accordéons autorise d’avoir plusieurs panels ouvert en même temps (ce qui est indiqué par l’attribut aria-multiselectable="true").

Sources

https://van11y.net/fr/accordeon-accessible/

La solution jQuery

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>jQuery UI Accordion - Default functionality</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.13.0/themes/base/jquery-ui.css">
  <link rel="stylesheet" href="/resources/demos/style.css">
  <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
  <script src="https://code.jquery.com/ui/1.13.0/jquery-ui.js"></script>
  <script>
  $( function() {
    $( "#accordion" ).accordion();
  } );
  </script>
</head>
<body>
 
<div id="accordion">
  <h3>Section 1</h3>
  <div>
    <p>
    Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer
    ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit
    amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut
    odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.
    </p>
  </div>
  <h3>Section 2</h3>
  <div>
    <p>
    Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet
    purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor
    velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In
    suscipit faucibus urna.
    </p>
  </div>
  <h3>Section 3</h3>
  <div>
    <p>
    Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis.
    Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero
    ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis
    lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.
    </p>
    <ul>
      <li>List item one</li>
      <li>List item two</li>
      <li>List item three</li>
    </ul>
  </div>
  <h3>Section 4</h3>
  <div>
    <p>
    Cras dictum. Pellentesque habitant morbi tristique senectus et netus
    et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in
    faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia
    mauris vel est.
    </p>
    <p>
    Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus.
    Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
    inceptos himenaeos.
    </p>
  </div>
</div>
 
 
</body>
</html>

Caroussel

La solution simple

Voici le résultat auquel on veut arriver :

Pour y parvenir, partez du code HTML suivant :

<div class="wrapper">
  <ul class="carousel" data-target="carousel">
    <li class="card" data-target="card"></li>
    <li class="card" data-target="card"></li>
    <li class="card" data-target="card"></li>
    <li class="card" data-target="card"></li>
    <li class="card" data-target="card"></li>
    <li class="card" data-target="card"></li>
    <li class="card" data-target="card"></li>
    <li class="card" data-target="card"></li>
    <li class="card" data-target="card"></li>
  </ul>
  <div class="button-wrapper">
    <button data-action="slideLeft">L</button>
    <button data-action="slideRight">R</button>
  </div>
</div>

Sans trop s’y attarder, regardons le CSS dont nous aurons besoin. Dans notre élément parent, il est primordial que le débordement soit masqué et que la largeur soit explicitement définie. De cette manière, l’élément parent sert de masque à toutes les cartes sous-jacentes du carrousel, et nous n’en voyons que trois à la fois.
J’ai choisi une largeur fixe ci-dessous car j’avais déjà décidé que la largeur de la carte était fixée à 200 pixels, avec une marge droite de 16 pixels. (200 * 3) + (16 * 2) = 632px pour la largeur du conteneur parent. N’hésitez pas à adopter une approche différente pour en arriver à masquer le contenu.

L’emballage des boutons sert à positionner les boutons de manière à ce qu’ils soient alignés avec chaque côté, centrés verticalement et positionnés sur le carrousel lui-même. Le reste des styles fait simplement en sorte que la liste de cartes défilent horizontalement, soit bien espacée et ne possèdent pas le style de liste par défaut d’un élément HTML.

.wrapper {
  height: 200px;
  width: 632px;
  position: relative;
  overflow: hidden;
  margin: 0 auto;
}

.button-wrapper {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  position: absolute;
}

.carousel {
  margin: 0;
  padding: 0;
  list-style: none;
  width: 100%;
  display: flex;
  position: absolute;
  left: 0;
  transition: all 1s ease;
}

.card {
  background: black;
  min-width: 200px;
  height: 200px;
  margin-right: 1rem;
  display: inline-block;
}

Commençons notre JavaScript par déclarer les variables qui nous seront utiles :

const carousel = document.querySelector("[data-target='carousel']");
const card = carousel.querySelector("[data-target='card']");
const leftButton = document.querySelector("[data-action='slideLeft']");
const rightButton = document.querySelector("[data-action='slideRight']");

Ensuite, nous avons besoin de quelques autres informations pour nous aider à faire glisser le carrousel de la bonne manière. L’effet souhaité est que le carrousel avance de 3 cartes à la fois et qu’il s’arrête à chaque fois dans la même position (aucune marge sur les côtés, avec les 3 cartes parfaitement visibles). Pour ce faire, nous avons besoin de connaître la largeur de la partie visible du carrousel et la propriété de marge droite assignée aux cartes.

Une chose qui est un peu délicate dans cette étape est la façon dont nous obtenons ces propriétés.

Malheureusement, vous ne pouvez pas utiliser “carrousel.width()” pour obtenir la largeur du carrousel. Au lieu de cela, vous devez utiliser carousel.offsetWidth.

Et pour la marge de la carte, c’est encore plus compliqué. Vous ne pourriez utiliser «card.style.marginRight()» que si vous définissez un style en ligne dans le code HTML, ce que nous n’avons pas fait ici.
Nous voulons récupérer le style que nous avons déclaré dans la feuille de style séparée.
Pour obtenir cette valeur, nous devons essayer de deux manières différentes d’obtenir le style défini extérieurement. La raison des deux approches est que différents navigateurs appellent cet objet différemment, nous devons donc essayer les deux méthodes.

Une fois que nous avons obtenu ces informations de style, nous pouvons cibler la propriété marginRight… mais il s’agit d’une chaîne, «16px». Nous devons donc faire deux choses: utiliser une expression régulière pour sélectionner uniquement les nombres, puis transformer la chaîne «16» en nombre 16.

const carouselWidth = carousel.offsetWidth;
const cardStyle = card.currentStyle || window.getComputedStyle(card)
const cardMarginRight = Number(cardStyle.marginRight.match(/\d+/g)[0]);

Nous devons maintenant définir les limites de notre carrousel. Bien que vous puissiez créer un carrousel «infini» qui répète le même contenu, nous choisissons ici une approche fixe. Le comportement que nous visons est donc lorsque la page est chargée et qu’il n’y a pas de contenu à gauche du carrousel, le bouton « Gauche » ne fonctionnera pas. Lorsque l’utilisateur atteindra l’extrémité du carrousel et qu’il n’y aura plus de contenu caché du côté droit, le bouton « Droite » ne fonctionnera plus.

Nous le ferons à l’aide de deux variables : offset et maxX.

offset est une variable qui augmente ou diminue de (carrouselWidth + cardMarginRight ou 648) chaque fois que nous cliquons sur l’un des boutons.
Le décalage augmente lorsque vous cliquez sur le bouton gauche (car nous allons pousser les cartes vers la droite de 648px à la fois), et diminuer lorsque vous cliquez sur le bouton droit (car nous allons tirer les cartes vers la gauche par 648px à la fois).
Lorsque la page est chargée, nous voulons que le carrousel commence au début, le décalage est donc initialisé à 0.

const cardCount = carousel.querySelectorAll("[data-target='card']").length;

let offset = 0;
const maxX = -((cardCount / 3) * carouselWidth + 
               (cardMarginRight * (cardCount / 3)) - 
               carouselWidth - cardMarginRight);

Enfin, nous ajoutons les écouteur d’événements de clic. Chaque écouteur inclut une logique qui permet d’empêcher le carrousel de « sortir » de sa fenêtre. Si les conditions sont vraies, nous mettons à jour « offset » par le carrouselWidth et le cardMarginRight (648). Ensuite, nous mettons à jour la propriété de transformation du carrousel en utilisant translateX ($ {offset}) px.

leftButton.addEventListener("click", function() {
  if (offset !== 0) {
    offset += carouselWidth + cardMarginRight;
    carousel.style.transform = `translateX(${offset}px)`;
    }
})
  
rightButton.addEventListener("click", function() {
  if (offset !== maxX) {
    offset -= carouselWidth + cardMarginRight;
    carousel.style.transform = `translateX(${offset}px)`;
  }
})

Et voila un carrousel fonctionnel simple avec JavaScript.

Et pour améliorer le comportement pour les utilisateurs dont le JavaScript est désactivé, ajoutez par défaut une classe « no-js » à l’élément body. Adaptez vos scripts et CSS pour que les panneaux ne soient pas masqués par défaut si le JavaScript est désactivé.

La vraie solution utilisable en production

Bonus

Le script fera tout le boulot et, à la volée :

  • Générera tous les id, les attributs ARIA nécessaires et liera tout cela ;
  • Créera les boutons « Suivant » et « Précédent » ;
  • Générera la liste de contrôles ;
  • Ajoutera les classes (namespacées si demandé), pour vous permettre de styler selon vos besoins ;
  • Ajoutera tous les listeners, etc.

Sources

https://van11y.net/fr/carrousel-accessible/

La solution jQuery

Utilisation d’un plugin dédié, comme par exemple :

Fenêtre modale

La solution simple

Voici le résultat auquel on veut arriver :

Pour y parvenir, partez du code HTML suivant :

<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.7.2/css/all.min.css'>

<div class="body-blackout"></div>

<button type="button" class="btn btn-sm btn-primary shadow p-2 px-3 popup-trigger" data-popup-trigger="one">
  Ouvrir la fenêtre modale
</button>

<div class="popup-modal shadow" data-popup-modal="one">
  <i class="fas fa-2x fa-times text-white bg-primary p-3 popup-modal__close"></i>
  <h1 class="font-weight-bold">
    Titre de la fenêtre modale
  </h1>
</div>

Vous pouvez utiliser les règles CSS suivantes :

.body-blackout {
  position: absolute;
  z-index: 1010;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.65);
  display: none;
}
.body-blackout.is-blacked-out {
  display: block;
}
.popup-trigger {
  display: inline-block;
}
.popup-modal {
  height: 365px;
  width: 650px;
  background-color: #fff;
  position: absolute;
  left: 50%;
  top: 50%;
  -webkit-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
  padding: 45px;
  opacity: 0;
  pointer-events: none;
  transition: all 300ms ease-in-out;
  z-index: 1011;
}
.popup-modal.is--visible {
  opacity: 1;
  pointer-events: auto;
}
.popup-modal__close {
  position: absolute;
  font-size: 1.2rem;
  right: -10px;
  top: -10px;
  cursor: pointer;
  background-color: #007bff !important;
  padding: 1rem !important;
}

La vraie solution utilisable en production

Sources

https://van11y.net/fr/modale-accessible/

La solution jQuery

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>jQuery UI Dialog - Default functionality</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.13.0/themes/base/jquery-ui.css">
  <link rel="stylesheet" href="/resources/demos/style.css">
  <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
  <script src="https://code.jquery.com/ui/1.13.0/jquery-ui.js"></script>
  <script>
  $( function() {
    $( "#dialog" ).dialog();
  } );
  </script>
</head>
<body>
 
<div id="dialog" title="Basic dialog">
  <p>This is the default dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the &apos;x&apos; icon.</p>
</div>
 
 
</body>
</html>

Infobulles

La solution simple

Voici le résultat auquel on veut arriver :

Pour y parvenir, partez du code HTML suivant :

<div class="demo">
  <p><a href="#" data-tooltip="Je suis le texte de l'infobulle !">Je suis un lien avec une infobulle.</a></p>

  <p><button data-tooltip="Je suis l'autre texte de l'autre infobulle !">Je suis un bouton avec une infobulle</button></p>
</div>

Vous pouvez utiliser les règles CSS suivantes :

/* Add this attribute to the element that needs a tooltip */
[data-tooltip] {
  position: relative;
  z-index: 2;
  cursor: pointer;
}

/* Hide the tooltip content by default */
[data-tooltip]:before,
[data-tooltip]:after {
  visibility: hidden;
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
  filter: progid: DXImageTransform.Microsoft.Alpha(Opacity=0);
  opacity: 0;
  pointer-events: none;
}

/* Position tooltip above the element */
[data-tooltip]:before {
  position: absolute;
  bottom: 150%;
  left: 50%;
  margin-bottom: 5px;
  margin-left: -80px;
  padding: 7px;
  width: 160px;
  -webkit-border-radius: 3px;
  -moz-border-radius: 3px;
  border-radius: 3px;
  background-color: #000;
  background-color: hsla(0, 0%, 20%, 0.9);
  color: #fff;
  content: attr(data-tooltip);
  text-align: center;
  font-size: 14px;
  line-height: 1.2;
}

/* Triangle hack to make tooltip look like a speech bubble */
[data-tooltip]:after {
  position: absolute;
  bottom: 150%;
  left: 50%;
  margin-left: -5px;
  width: 0;
  border-top: 5px solid #000;
  border-top: 5px solid hsla(0, 0%, 20%, 0.9);
  border-right: 5px solid transparent;
  border-left: 5px solid transparent;
  content: " ";
  font-size: 0;
  line-height: 0;
}

/* Show tooltip content on hover */
[data-tooltip]:hover:before,
[data-tooltip]:hover:after {
  visibility: visible;
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
  filter: progid: DXImageTransform.Microsoft.Alpha(Opacity=100);
  opacity: 1;
}

La vraie solution utilisable en production

Sources

https://van11y.net/fr/infobulles-tooltips-accessibles/

La solution jQuery

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>jQuery UI Tooltip - Default functionality</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.13.0/themes/base/jquery-ui.css">
  <link rel="stylesheet" href="/resources/demos/style.css">
  <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
  <script src="https://code.jquery.com/ui/1.13.0/jquery-ui.js"></script>
  <script>
  $( function() {
    $( document ).tooltip();
  } );
  </script>
  <style>
  label {
    display: inline-block;
    width: 5em;
  }
  </style>
</head>
<body>
 
<p><a href="#" title="That&apos;s what this widget is">Tooltips</a> can be attached to any element. When you hover
the element with your mouse, the title attribute is displayed in a little box next to the element, just like a native tooltip.</p>
<p>But as it&apos;s not a native tooltip, it can be styled. Any themes built with
<a href="http://jqueryui.com/themeroller/" title="ThemeRoller: jQuery UI&apos;s theme builder application">ThemeRoller</a>
will also style tooltips accordingly.</p>
<p>Tooltips are also useful for form elements, to show some additional information in the context of each field.</p>
<p><label for="age">Your age:</label><input id="age" title="We ask for your age only for statistical purposes."></p>
<p>Hover the field to see the tooltip.</p>
 
 
</body>
</html>