热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

BootStrapTooltip插件源码解析

这篇文章主要为大家详细解析了BootStrap的Tooltip插件源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

Tooltip插件可以让你把要显示的内容以弹出框的形式来展示,如:

因为自己在工作的过程中,用到了Tooltip这个插件,并且当时正想学习一下元素定位的问题,如:提示框显示的位置就是触发提示框元素的位置,可以配置在上、下、左、右等位置,所以就去看了源码。对于整个插件源码没有看全,但也学到了许多的知识点。能力有限,可能其中有认识错误的地方,以后再补充吧

1 使用方法不介绍 ,可以参照

Bootstrap 提示工具(Tooltip)插件

2 源码解析

+function ($) {
 'use strict';

 // TOOLTIP PUBLIC CLASS DEFINITION
 // ===============================


 var Tooltip = function (element, options) {
  this.type    =
  this.optiOns=
  this.enabled  =
  this.timeout  =
  this.hoverState =
  this.$element  = null

  this.init('tooltip', element, options)
 }

 Tooltip.VERSION = '3.3.0'

 Tooltip.TRANSITION_DURATION = 150

 //默认参数
 Tooltip.DEFAULTS = {
  animation: true,
  placement: 'top',
  selector: false,
  template: '',
  trigger: 'hover focus',
  title: '',
  delay: 0,
  html: false,
  container: false,
  viewport: {
   selector: 'body',
   padding: 0
  }
 }

 //初始化
 Tooltip.prototype.init = function (type, element, options) {
  this.enabled  = true
  this.type   = type
  this.$element = $(element)
  //初始化参数
  this.optiOns= this.getOptions(options)

  this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)

  //多个触发器 即触发函数
  var triggers = this.options.trigger.split(' ')


  for (var i = triggers.length; i--;) {
   var trigger = triggers[i]

   if (trigger == 'click') {
    //绑定事件处理程序
    //事件命名空间 'click.tooltip'
    //触发时执行$.proxy(this.toggle, this)返回的函数
    this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
   } else if (trigger != 'manual') {
    var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
    var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'

    this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
    this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
   }
  }

  this.options.selector ?
   (this._optiOns= $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
   this.fixTitle()
 }

 Tooltip.prototype.getDefaults = function () {
  return Tooltip.DEFAULTS
 }

 //获得初始化参数
 Tooltip.prototype.getOptiOns= function (options) {
  // this.$element.data() data-key = value 形式的属性获取 
  // 返回一个对象{key:value}
  // options > 元素属性 > 默认 
  optiOns= $.extend({}, this.getDefaults(), this.$element.data(), options)

  //delay 为数字 特殊处理
  //delay 为对象 delay:{ show: 500, hide: 100 }
  if (options.delay && typeof options.delay == 'number') {
   options.delay = {
    show: options.delay,
    hide: options.delay
   }
  }

  return options
 }

 Tooltip.prototype.getDelegateOptiOns= function () {
  var optiOns= {}
  var defaults = this.getDefaults()

  this._options && $.each(this._options, function (key, value) {
   if (defaults[key] != value) options[key] = value
  })

  return options
 }

 //提示框显示
 Tooltip.prototype.enter = function (obj) {
  //obj是否是tooltip的实例
  var self = obj instanceof this.constructor ?
   obj : $(obj.currentTarget).data('bs.' + this.type)

  //$tip.is(':visible') 该div是否可见
  //可见时 只更新状态 然后直接返回
  if (self && self.$tip && self.$tip.is(':visible')) {
   self.hoverState = 'in'
   return
  }

  //self 不存在
  if (!self) {
   self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
   $(obj.currentTarget).data('bs.' + this.type, self)
  }

  //上次点击延时未显示 则取消显示
  clearTimeout(self.timeout)

  //设置状态
  self.hoverState = 'in'

  //delay: 数字 (默认 0) 或  对象 { show: 500, hide: 100 }
  //没有delay delay.show 时 直接显示 
  if (!self.options.delay || !self.options.delay.show) return self.show()

  //延时显示 self.options.delay.show:延时时间
  self.timeout = setTimeout(function () {
   if (self.hoverState == 'in') self.show()
  }, self.options.delay.show)
 }

 //提示框隐藏
 Tooltip.prototype.leave = function (obj) {
  var self = obj instanceof this.constructor ?
   obj : $(obj.currentTarget).data('bs.' + this.type)

  if (!self) {
   self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
   $(obj.currentTarget).data('bs.' + this.type, self)
  }

  clearTimeout(self.timeout)

  self.hoverState = 'out'

  if (!self.options.delay || !self.options.delay.hide) return self.hide()

  self.timeout = setTimeout(function () {
   if (self.hoverState == 'out') self.hide()
  }, self.options.delay.hide)
 }

 //提示框显示核心方法
 Tooltip.prototype.show = function () {
  console.info(this) 
  var e = $.Event('show.bs.' + this.type)

  if (this.hasContent() && this.enabled) {
   this.$element.trigger(e)

   //判断是否在根节点中
   //this.$element[0] 转换为DOM对象
   //.ownerDocument Document文档对象
   //.documentElement 根节点 HTML标签
   //$.contains 一个DOM节点是否包含另一个DOM节点。
   var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])

   //调用preventDefault 或 不在根节点 return
   //事件对象中是否调用过 event.preventDefault()
   //event.preventDefault():阻止元素发生默认的行为
   //如 当点击提交按钮时阻止对表单的提交 、 阻止以下 URL 的链接
   if (e.isDefaultPrevented() || !inDom) return
   var that = this

   var $tip = this.tip()

   var tipId = this.getUID(this.type)
   //设置提示框的title
   this.setContent()
   $tip.attr('id', tipId)
   this.$element.attr('aria-describedby', tipId)
   //参数animation
   if (this.options.animation) $tip.addClass('fade')

   //参数placement 提示框的位置 top bottom left right
   var placement = typeof this.options.placement == 'function' ?
    this.options.placement.call(this, $tip[0], this.$element[0]) :
    this.options.placement

   // 判断placement是否包含"auto"
   var autoToken = /\s?auto?\s?/i
   var autoPlace = autoToken.test(placement)
   //包含 "auto"时
   if (autoPlace) placement = placement.replace(autoToken, '') || 'top'

   $tip
    //detach() 删除匹配的对象 与remove()不同的是,所有绑定的事件、附加的数据等都会保留下来
    .detach()
    .css({ top: 0, left: 0, display: 'block' })
    .addClass(placement)
    .data('bs.' + this.type, this)
   //参数 container 向指定元素添加该提示框
   //没有 添加到当前元素后面
   this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
   //位置对象pos pos = {top: , right: , bottom: , left: , width: ,scroll:}
   var pos     = this.getPosition()
   //提示框自身实际的 width height
   var actualWidth = $tip[0].offsetWidth
   var actualHeight = $tip[0].offsetHeight

   if (autoPlace) {
    var orgPlacement = placement
    var $cOntainer= this.options.container ? $(this.options.container) : this.$element.parent()
    var cOntainerDim= this.getPosition($container)

    placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top'  :
          placement == 'top'  && pos.top  - actualHeight  containerDim.width ? 'left'  :
          placement == 'left'  && pos.left  - actualWidth  viewportDimensions.top + viewportDimensions.height) { // bottom overflow
    delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
   }
  } else {

   var leftEdgeOffset = pos.left - viewportPadding
   var rightEdgeOffset = pos.left + viewportPadding + actualWidth
   if (leftEdgeOffset  viewportDimensions.width) { // right overflow
    delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
   }
  }

  return delta
 }

 //提示框的title
 Tooltip.prototype.getTitle = function () {
  var title
  var $e = this.$element
  var o = this.options
  //先获取当前元素的data-original-title属性
  //否则获取参数title
  title = $e.attr('data-original-title')
   || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)

  return title
 }

 //随机生成唯一id 0 -- 100000 id值  
 Tooltip.prototype.getUID = function (prefix) {
  //~~ 去掉小数部分
  do prefix += ~~(Math.random() * 1000000)
  while (document.getElementById(prefix))
  return prefix
 }

 //生成提示框的div
 //不存在 则用模板
 //
 Tooltip.prototype.tip = function () {
  return (this.$tip = this.$tip || $(this.options.template))
 }

 Tooltip.prototype.arrow = function () {
  return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
 }

 Tooltip.prototype.enable = function () {
  this.enabled = true
 }

 Tooltip.prototype.disable = function () {
  this.enabled = false
 }

 Tooltip.prototype.toggleEnabled = function () {
  this.enabled = !this.enabled
 }

 //click 事件触发时执行的函数
 Tooltip.prototype.toggle = function (e) {
  //this tooltip实例对象
  var self = this
  if (e) {
   //e.currentTarget 返回注册该事件处理程序的元素
   self = $(e.currentTarget).data('bs.' + this.type)
   if (!self) {
    self = new this.constructor(e.currentTarget, this.getDelegateOptions())
    $(e.currentTarget).data('bs.' + this.type, self)
   }
  }
  //判断提示框当前的状态
  //true 当前显示 需要隐藏
  //false 相反
  self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
 }

 Tooltip.prototype.destroy = function () {
  var that = this
  clearTimeout(this.timeout)
  this.hide(function () {
   that.$element.off('.' + that.type).removeData('bs.' + that.type)
  })
 }


 // TOOLTIP PLUGIN DEFINITION
 // =========================

 function Plugin(option) {
  return this.each(function () {
   var $this  = $(this)
   //所选元素是否有 key为:bs.tooltip 对应的value值
   var data   = $this.data('bs.tooltip')
   //option为对象 options:option
   //option为字符串 则是方法 options:false 
   var optiOns= typeof option == 'object' && option
   //option是对象 执行初始化 ,并提供了选择器
   var selector = options && options.selector

   //是destroy 方法 不执行
   if (!data && option == 'destroy') return
   //tooltip初始化
   if (selector) {
    //存在选择器,则将当前点击元素 bs.tooltip = {} 即去掉Tooltip实例
    if (!data) $this.data('bs.tooltip', (data = {}))
    //给选择器加上Tooltip实例
    if (!data[selector]) data[selector] = new Tooltip(this, options)

   } else {
    //没有value值时,则加上 bs.tooltip = data(new Tooltip(this, options)) 
    if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
   }

   //执行方法
   if (typeof option == 'string') data[option]()
  })
 }

 //缓存以前$.fn上的tooltip
 var old = $.fn.tooltip

 //添加到jQuery对象上
 $.fn.tooltip       = Plugin
 $.fn.tooltip.COnstructor= Tooltip


 // TOOLTIP NO CONFLICT
 // ===================

 //tooltip冲突时 调用该方法
 //var other = $("#aa").tooltip().noConflict();
 $.fn.tooltip.noCOnflict= function () {
  $.fn.tooltip = old
  return this
 }

}(jQuery);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • Spring Boot 中静态资源映射详解
    本文深入探讨了 Spring Boot 如何简化 Web 应用中的静态资源管理,包括默认的静态资源映射规则、WebJars 的使用以及静态首页的处理方法。通过本文,您将了解如何高效地管理和引用静态资源。 ... [详细]
  • 本文探讨了2019年前端技术的发展趋势,包括工具化、配置化和泛前端化等方面,并提供了详细的学习路线和职业规划建议。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文详细介绍了如何使用Python编写爬虫程序,从豆瓣电影Top250页面抓取电影信息。文章涵盖了从基础的网页请求到处理反爬虫机制,再到多页数据抓取的全过程,并提供了完整的代码示例。 ... [详细]
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • 深入理解ExtJS:从入门到精通
    本文详细介绍了ExtJS的功能及其在大型企业前端开发中的应用。通过实例和详细的文件结构解析,帮助初学者快速掌握ExtJS的核心概念,并提供实用技巧和最佳实践。 ... [详细]
  • 本文汇集了一系列具有强烈设计感的网站模板,特别是来自知名平台WrapBootstrap的响应式网站模板。这些模板不仅美观,而且功能强大,适合各种类型的网站建设需求。 ... [详细]
  • Asp.net MVC 中 Bundle 配置详解:合并与压缩 JS 和 CSS 文件
    本文深入探讨了 Asp.net MVC 中如何利用 Bundle 功能来合并和压缩 JavaScript 和 CSS 文件,提供了详细的配置步骤和示例代码,适合开发人员参考学习。 ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
author-avatar
我不是不是小受
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有