bootstrap插件学习-bootstrap.tooltip.js

时间:2023-12-04 11:30:08

先看bootstrap-tooltip.js的结构

var Tooltip = function ( element, options ){} // 构造器
Tooltip.prototype ={} //构造器的原型
$.fn.tooltip = function ( option ) {} //jQuery原型上自定义的方法
$.fn.tooltip.Constructor = Tooltip //重置jQuery原型方法tooltip的构造器名
$.fn.tooltip.defaults ={} // 默认参数

因为tooltip插件的使用比较多,调用者比较杂,源码中没有给出初始化的步骤,在看源码的过程中,我们手动添加初始化。

<p class="muted">
“这是我的第一次英文访问,很抱歉它不够严谨,但是我不得不这么做,不只因为采访时间限制,更因为我面对的是卡梅隆,这个人喜爱挑战、从无畏惧,他也希望别人如此,他可以原谅不完美,但他无法接受一个人不去努力接近自己的极限。” ——
<a rel="tooltip" href="#" data-original-title="柴静始终站在离新闻最近的地方,她以她的犀利和敏锐、坚定与坚持,最终历练成为一名优秀的新闻工作者。 ">柴静</a>
《看见》专访
<a id="a1" rel="tooltip" href="#" data-original-title="1954年8月16日生于加拿大的著名电影导演,擅长拍摄动作片以及科幻电影。">卡梅隆</a>
</p>
$("#a1").tooltip();

在script标签部分加入以上代码,我们可以鼠标移入'卡梅隆'时显示提示框了。

下面开始,进入jQuery原型上的自定义方法tooltip中

/*
* jQuery原型上自定义的方法
* */
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tooltip')
, options = typeof option == 'object' && option
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))//实例化构造器
if (typeof option == 'string') data[option]()//支持传入方法名参数,执行该方法。
})
}

跟之前的几个插件类似,进入构造器中。

/*
* 构造器
* */
var Tooltip = function ( element, options ) {
this.init('tooltip', element, options)//实例化直接调用原型的init方法
}

进入原型上的init方法

init: function ( type, element, options ) {
var eventIn
, eventOut this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true if (this.options.trigger != 'manual') { //选择事件名
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
//绑定事件 this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))//其中有false,达到阻止冒泡的功能
this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
} this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()//没有默认参数执行
}

init方法一般执行初始化的步骤,其中的getOptions方法

/*
* 添加默认项
* */
, getOptions: function ( options ) {
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay
, hide: options.delay
}
} return options
}

这里我们看到init方法为点击标签绑定了两种事件mouseenter和mouseleave,它们与mouseover和mouseout的区别:

不论鼠标指针穿过被选元素或其子元素,都会触发 mouseover 事件。

只有在鼠标指针穿过被选元素时,才会触发 mouseenter 事件。

绑定完事件之后,如果是无参数时,我们执行fixTitle方法

/*
* 将被点击标签的title属性值转给data-original-title属性,最后删除title属性
* */
, fixTitle: function () {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
}
}

为data-original-title属性赋值,为以后将该值加如提示框中做准备。

当鼠标移入时,触发mouseenter事件,进入绑定好的enter方法中

/*
* 目的调用show方法
* */
, enter: function ( e ) {
/*
* 以下方法执行了jQuery原型上的tooltip方法。通过each遍历,获取data对象中的tooltip属性:即该对象的jQuery对象
* 感觉这个写法蛋疼
* */
var self = $(e.currentTarget)[this.type](this._options).data(this.type)//e.currentTarget获取当前点击对象 if (!self.options.delay || !self.options.delay.show) {
self.show()//鼠标移上去执行show方法
} else {
self.hoverState = 'in'
setTimeout(function() {
if (self.hoverState == 'in') {
self.show()
}
}, self.options.delay.show)
}
}

纵观这个方法,其主要目的是为了调用show方法。

/*
* 显示提示框
* */
, show: function () {
var $tip
, inside
, pos
, actualWidth
, actualHeight
, placement
, tp if (this.hasContent() && this.enabled) {
$tip = this.tip()//获取提示框的jQuery对象
this.setContent()//给提示框赋值,初始化提示框的样式 if (this.options.animation) {
$tip.addClass('fade')//提示框拥有运动效果,加入fade类
} placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement//控制提示框的显示方位,默认为top inside = /in/.test(placement)//是否是in $tip
.remove()
.css({ top: 0, left: 0, display: 'block' })
.appendTo(inside ? this.$element : document.body)
/*先将提示框从文档中删除,获取其返回对象,即本身,加入样式,最后再重新插回文档中,这么写主要考虑in的情况
* 符合一种操作规范,如果添加样式,操作dom过于复杂,可以先将节点从dom中取出,经过一系列装饰后,再加入dom中,第一
* 便于浏览器渲染,二来也符合dom操作规范
* */
pos = this.getPosition(inside)//获取被点击对象的位置和本身尺寸
//获取提示框的宽度和高度
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
//我们拿top举例,在a标签上方,left方向,对着a标签居中。
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
break
} $tip
.css(tp)
.addClass(placement)//配合内部的div实现小三角效果
.addClass('in')//半透明效果
}
}

show方法是这个插件的核心方法,与show相关的几个方法,tip()方法,获得提示框模版,setContent()为提示框赋值,hasContent()中调用getTitle()方法获取被点击标签的获取data-original-title属性的值。逻辑不复杂,插件相关的方法调用比较多,耐心点看。

/*
* 调用getTitle方法
* */
, hasContent: function () {
return this.getTitle()
}
/*
* 获取data-original-title属性的值
* */
, getTitle: function () {
var title
, $e = this.$element
, o = this.options title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
title = (title || '').toString().replace(/(^\s*|\s*$)/, "")//将title中的|去除,并且将|两边的文字合并 return title
}

tip方法

/*
* 返回提示框模版的jQuery对象
* */
, tip: function () {
return this.$tip = this.$tip || $(this.options.template)
}

为提示框赋内容

/*
* 往提示框中添加title信息,初始化提示框的样式
* */
, setContent: function () {
var $tip = this.tip()
$tip.find('.tooltip-inner').html(this.getTitle())//添加内容信息
$tip.removeClass('fade in top bottom left right')
}

调用getPosition方法,获得对象的位置和尺寸

/*
* 获取对象的高度和宽度,和偏移值(left和top)
* */
, getPosition: function (inside) {
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
}

offsetWidth和offsetHeight是javascript元素的属性。表示内容高度+内边距+边框,内容宽度+内边距+边框。

对于提示框的显示,默认在被点击标签的上方。相对于被点击标签居中。

当鼠标移开时,触发原型上的leave方法

/*
* 调用hide方法
* */
, leave: function ( e ) {
/*
* 与enter中一样
* */
var self = $(e.currentTarget)[this.type](this._options).data(this.type) if (!self.options.delay || !self.options.delay.hide) {
self.hide()
} else {
self.hoverState = 'out'
setTimeout(function() {
if (self.hoverState == 'out') {
self.hide()
}
}, self.options.delay.hide)
}
}

进入核心方法hide

hide: function () {
var that = this
, $tip = this.tip() $tip.removeClass('in')
/*
* 定义了提示框消失的特效,要显示这个特效,需要引入其他js,这先不讨论
* */
function removeWithAnimation() {
var timeout = setTimeout(function () {
$tip.off($.support.transition.end).remove()
}, 500) $tip.one($.support.transition.end, function () {
clearTimeout(timeout)
$tip.remove()
})
} $.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.remove()//直接删除提示框
}

我们没有引入相关js时,提示框直接删除了。

有兴趣的朋友可以研究一下bootstrap.tooltip插件对于提示框样式的编写,也挺不错的。最后插件还提供了几个方法,便于我们以后扩展。

/*
* 开启提示框功能
* */
, enable: function () {
this.enabled = true
} /*
* 禁用提示框功能
* */
, disable: function () {
this.enabled = false
}
/*开启/禁用切换*/
, toggleEnabled: function () {
this.enabled = !this.enabled
}
/*提示框显示隐藏切换*/
, toggle: function () {
this[this.tip().hasClass('in') ? 'hide' : 'show']()
} validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}

使用的时候,直接用jQuery取得一个dom对象点就能点出上述的方法了,很方便。

内容不多,时间刚好,以上是我的一点读码体会,如有错误,请指出,大家共通学习。