popper.js——tooltip文档及源码分析

popper.js之tooltip文档及源码分析

popper说明

由于popper.js这个名字比较大众化,有必要说明一下具体指的是哪一个;
没错就是你想的这个:
https://popper.js.org/tooltip-examples.html
https://github.com/FezVrasta/popper.js

popper 是一个定位引擎,就是说他只做一件事使指定节点定位到指定位置。至于DOM的显示、隐藏、内容、外观、丑美胖瘦……他都不管

那tooltip.js和popper是什么关系呢?
可以说是理论与实践的关系,tooltip是对popper的具体应用。想想是不是很兴奋,什么下拉菜单、select表单模拟、提示工具……总之,一切弹出定位的UI组件都可以通过这个引擎实现。

tooltip文档简单说明

调用方式

new Tooltip(element,options);

参数说明

element

  • DOM节点 :如 document.getElementById(el)
  • jquery 对象 : 如 $(el) , 默认 取第0个元素

options
选项既可以在js初始化时传入,也可以在元素上通过data-绑定

选项 类型 说明
placement String 弹出位置 默认:bottom;top(-start, -end), right(-start, -end), bottom(-start, -end), left(-start, -end)
arrowSelector String 设置三角的class 默认:.tooltip-arrow, .tooltip__arrow
innerSelector String 设置内容容器的class 默认:.tooltip-inner, .tooltip__inner
container HTMLElement、String、false 将tooltip元素插入到container元素 默认:false (参照元素的父节点)
delay Number、Object 延迟显示或隐藏 ,不适用手动触发类型; 默认:0;如果是一个值(Number),同时被应用到显示和隐藏;如果是对象{ show: 500, hide: 100 },则分别被应用到显示和隐藏
html Boolean 把title中的内容以text(false)或者html(true)格式插入到tooltip-inner里 默认:false
template String 插入的模板内容 默认:
title String、HTMLElement、TitleFunction 弹出的具体内容title
trigger String 触发方式:多种触发方式,中间用空格隔开;manual 手动,不能与其他方式组合使用; 默认:hover focus;可选hover、focus、click、manual
closeOnClickOutside Boolean 是否在点击弹出元素和参照元素以外,关闭弹出元素;只适用于click方式 默认:false
boundariesElement String、HTMLElement 设置弹出元素的边界
offset String、Number 相对参照元素的偏移量设置 默认:0
popperOptions Object 直接设置popper实例的选项

tooltip源码注释

import Popper from 'popper.js';
import isFunction from '../../popper/src/utils/isFunction';

const DEFAULT_OPTIONS = {
  container: false,
  delay: 0,
  html: false,
  placement: 'top',
  title: '',
  template:
    '',
  trigger: 'hover focus',
  offset: 0,
  arrowSelector: '.tooltip-arrow, .tooltip__arrow',
  innerSelector: '.tooltip-inner, .tooltip__inner',
};

export default class Tooltip {

  constructor(reference, options) {
    // apply user options over default ones
    // 用户选项覆盖默认选项
    options = { ...DEFAULT_OPTIONS, ...options };

    // 如果是jq对象,取第一个————转成js节点
    reference.jquery && (reference = reference[0]);

    // cache reference and options
    // 缓存参照元素和选项
    this.reference = reference;
    this.options = options;

    // get events list  
    // 获取触发方式的数组列表
    // events 是数组,触发事件的数组
    const events =
      typeof options.trigger === 'string'
        ? options.trigger
            .split(' ')
            .filter(
              trigger => ['click', 'hover', 'focus'].indexOf(trigger) !== -1
            )
        : [];

    // set initial state
    // 设置出事状态
    // 显示或隐藏。默认隐藏
    this._isOpen = false;
    // Popper实例选项
    this._popperOptions = {};

    // set event listeners
    // 设置事件监听
    this._setEventListeners(reference, events, options);
  }

  //
  // Public methods
  //

  /**
   * Reveals an element's tooltip. This is considered a "manual" triggering of the tooltip.
   * Tooltips with zero-length titles are never displayed.
   * @method Tooltip#show
   * @memberof Tooltip
   */
   // manual 手动触发
  show = () => this._show(this.reference, this.options);

  /**
   * Hides an element’s tooltip. This is considered a “manual” triggering of the tooltip.
   * @method Tooltip#hide
   * @memberof Tooltip
   */
   // manual 手动触发
  hide = () => this._hide();

  /**
   * Hides and destroys an element’s tooltip.
   * @method Tooltip#dispose
   * @memberof Tooltip
   */
   // manual 手动触发
  dispose = () => this._dispose();

  /**
   * Toggles an element’s tooltip. This is considered a “manual” triggering of the tooltip.
   * @method Tooltip#toggle
   * @memberof Tooltip
   */
   // manual 手动触发
  toggle = () => {
    if (this._isOpen) {
      return this.hide();
    } else {
      return this.show();
    }
  };

  /**
   * Updates the tooltip's title content
   * @method Tooltip#updateTitleContent
   * @memberof Tooltip
   * @param {String|HTMLElement} title - The new content to use for the title
   */
   // manual 手动触发
  updateTitleContent = (title) => this._updateTitleContent(title);

  //
  // Private methods
  //
  // 私有方法列表

  _events = [];

  /**
   * Creates a new tooltip node
   * @memberof Tooltip
   * @private
   * @param {HTMLElement} reference
   * @param {String} template
   * @param {String|HTMLElement|TitleFunction} title
   * @param {Boolean} allowHtml
   * @return {HTMLElement} tooltipNode
   */
  _create(reference, template, title, allowHtml) {
    // create tooltip element
    // 创建一个div元素,里面插入template内容
    const tooltipGenerator = window.document.createElement('div');
    tooltipGenerator.innerHTML = template.trim();
    // 获取到模板中的第一个子节点————class=“tooltip”
    const tooltipNode = tooltipGenerator.childNodes[0];

    // add unique ID to our tooltip (needed for accessibility reasons)
    // 给class=“tooltip”添加一个随机ID
    tooltipNode.id = `tooltip_${Math.random()
      .toString(36)
      .substr(2, 10)}`;

    // set initial `aria-hidden` state to `false` (it's visible!)
    // 设置属性为可见的
    tooltipNode.setAttribute('aria-hidden', 'false');

    // add title to tooltip
    // 获取title节点,————tooltip-inner(现在里面是空的)
    const titleNode = tooltipGenerator.querySelector(this.options.innerSelector);
    // 给title节点添加内容
    this._addTitleContent(reference, title, allowHtml, titleNode);

    // return the generated tooltip node
    // 返回新创建div里的内容,也就是class tooltip 节点
    return tooltipNode;
  }

  _addTitleContent(reference, title, allowHtml, titleNode) {
    if (title.nodeType === 1 || title.nodeType === 11) {
      // if title is a element node or document fragment, append it only if allowHtml is true
      // 如果title内容是元素节点或者 #document 片段 
      // 如果option.html=true,允许插入html类型
      // 把title内容插入title节点(tooltip-inner)
      allowHtml && titleNode.appendChild(title);
    } else if (isFunction(title)) {
      // if title is a function, call it and set textContent or innerHtml depending by `allowHtml` value
      // 如果title是一个函数;
      // titleText 调用函数,并且把this指向参照元素,获取title函数的返回值,
      const titleText = title.call(reference);
      // 如果option.html=true,允许插入html类型--> 则插入title的所有内容;
      // 否则插入title中的所有文本,包括