Dans ce tutoriel je vous propose de découvrir comment créer une infobulle en utilisant du JavaScript (avec la syntaxe ES2015).

Pourquoi pas en CSS

Il est possible de créer une petite bulle d'information en utilisant simplement du CSS. Cela fonctionne dans des cas simples mais il est difficile d'étendre le système (pour accueillir de l'HTML par exemple).

L'algorithme

Avant de nous lancer dans le code il est important de réfléchir au déroulement de notre algorithme :

/*
On sélectionne les éléments avec un attribut particulier (a[title])
Lorsque l'on survole un élément {
  On crée une <diV> dans le <body>
  On remplit la <div> avec le texte correspondant au titre
  On positionne la bulle au dessus de l'élément
  On ajoute une classe, pour animer l'apparition
}
Lorsque l'on quitte le survole {
  On retire une classe, pour animer la disparition
  Lorsque l'animation se termine {
    On supprime la <div> du <body>
  }
}
*/

Pour avoir un code simple et adaptable, nous allons utiliser une classe qui fonctionnera de la manière suivante :

Tooltip.bind('a[title]')

Pour le moment le système va fonctionner avec l'attribut title d'un lien ou un attribut data-tooltip mais il est possible d'ajouter des comportements supplémentaires en modifiant le comportement du constructeur.

class Tooltip {
  /**
   * Applique le système de bulle d'infos sur les éléments
   * @param {string} selector 
   */
  static bind (selector) {
    document.querySelectorAll(selector).forEach(element => new Tooltip(element))
  }

  /**
   * @param {HTMLElement} element 
   */
  constructor (element) {
    this.element = element
    let tooltipTarget = this.element.getAttribute('data-tooltip')
    if (tooltipTarget) {
      this.title = document.querySelector(tooltipTarget).innerHTML
    } else {
      this.title = element.getAttribute('title')
    }
    this.tooltip = null
    this.element.addEventListener('mouseover', this.mouseOver.bind(this))
    this.element.addEventListener('mouseout', this.mouseOut.bind(this))
  }

  mouseOver () {
    let tooltip = this.createTooltip()
    let width = tooltip.offsetWidth
    let height = tooltip.offsetHeight
    let left = this.element.offsetWidth / 2 - width / 2 + this.element.getBoundingClientRect().left + document.documentElement.scrollLeft
    let top = this.element.getBoundingClientRect().top - height - 15 + document.documentElement.scrollTop
    if (left < 20) {
      left = 20
    }
    tooltip.style.left = left + "px"
    tooltip.style.top = top + "px"
    tooltip.classList.add('visible')
  }

  mouseOut () {
    if (this.tooltip !== null) {
      this.tooltip.classList.remove('visible')
      this.tooltip.addEventListener('transitionend', () => {
        if (this.tooltip !== null) {
          document.body.removeChild(this.tooltip)
          this.tooltip = null
        }
      })
    }
  }

  /**
   * Crée et injecte la bulle d'info dans l'HTML
   * @returns {HTMLElement}
   */
  createTooltip () {
    if (this.tooltip === null) {
      let tooltip = document.createElement('div')
      tooltip.innerHTML = this.title
      tooltip.classList.add('tippy')
      document.body.appendChild(tooltip)
      this.tooltip = tooltip
    }
    return this.tooltip
  }
}