5.7.3 源码分析

我们前面说了,弹出框和提示框几乎一模一样,而且弹出框是从提示框继承而来的,所以继承以后唯一要处理的不同的内容,就是上面说的两个不同点:

❑将默认事件设置为click。

❑模板设置步骤要重新写,因为设置了两个内容。

步骤1 立即调用的函数。此步骤与Modal插件的步骤1一样,此处不赘述。

步骤2 插件核心代码。主要是popover核心类函数的定义、默认参数设置、继承tooltip的原型方法、重载要处理不同逻辑的个别方法。核心摘要代码如下:

  1. var Popover = function (element, options) {
  2. this.init('popover', element, options) // 调用了从tooltip继承过来的原型方法
  3. // init,并传入了popover类型
  4. }
  5. // 如果tooltip没引用,抛错,因为其依赖于tooltip
  6. if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
  7.  
  8. // 除了继承tooltip的默认值外,下面的默认值覆盖了tooltip的默认值
  9. Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
  10. placement: 'right', // 显示位置,默认在右方显示
  11. trigger: 'click', // 设置触发popover的事件
  12. content: '', // 默认显示内容
  13. template: '<div class="popover"><div class="arrow"></div><h3 class="popover-
  14. title"></h3><div class="popover-content"></div></div>' // popover显示的内容模板
  15. })
  16.  
  17. // 继承tooltip的原型,含所有原型方法
  18. Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
  19.  
  20. Popover.prototype.constructor = Popover // 恢复构造函数,以免使用tooltip的构造函数
  21. // 重载tooltip里的getDefaults方法:获取默认配置
  22. Popover.prototype.getDefaults = function () {};
  23. // 重载tooltip里的setContent方法:设置popover要显示的内容
  24. Popover.prototype.setContent = function (){};
  25. // 重载tooltip里的hasContent方法:判断popover是否有要显示的内容
  26. Popover.prototype.hasContent = function (){};
  27. // 重载tooltip里的getContent方法:获取要显示的content内容
  28. Popover.prototype.getContent = function (){};
  29. // 重载tooltip里的arrow方法:获取显示的小箭头
  30. Popover.prototype.arrow = function (){};
  31. // 重载tooltip里的tip方法:获取模板内容
  32. Popover.prototype.tip = function (){};

通过上述概要代码可以看出,就像我们从其他文章里看到的一样,首先继承了tooltip的原型,然后重新赋值其构造函数,最后再重载6个原型方法,分别用于处理:默认值、内容设置、小箭头设置等。详细源码如下:

  1. // 重载tooltip里的getDefaults方法:获取默认配置
  2. Popover.prototype.getDefaults = function () {
  3. return Popover.DEFAULTS
  4. }
  5. // 重载tooltip里的setContent方法:设置popover要显示的内容
  6. Popover.prototype.setContent = function () {
  7. var $tip = this.tip() // 获取模板内容
  8. var title = this.getTitle() // 获取title内容
  9. var content = this.getContent() // 获取要显示的内容
  10.  
  11. // 给模板里的popover-title样式的元素添加内容,如果支持HTML,就设置HTML,否则设置text
  12. $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
  13. $tip.find('.popover-content')[ // 对于HTML对象使用append,用于维护JS事件
  14. this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
  15. ](content)
  16.  
  17. // 正式显示之前,如果有多余的样式,全部删除,后面会根据状态再添加
  18. $tip.removeClass('fade top bottom left right in')
  19.  
  20. // 由于IE8不支持empty伪类选择器,所以需要手动判断:如果title没有值,则隐藏该元素
  21. if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
  22. }
  23. // 重载tooltip里的hasContent方法:判断popover是否有要显示的内容
  24. Popover.prototype.hasContent = function () {
  25. return this.getTitle() || this.getContent() // title和content任何一个有内容,
  26. 即认为popover有内容
  27. }
  28. // 重载tooltip里的getContent方法:获取要显示的content内容
  29. Popover.prototype.getContent = function () {
  30. var $e = this.$element // 所单击的触发元素
  31. var o = this.options // 传入的选项
  32.  
  33. // 如果data-content属性有值,就使用它作为内容
  34. // 否则,再判断如果options里的content属性是function,就将其调用结果作为内容
  35. // 如果上述两者都不是,直接将options里的content属性作为内容返回
  36. return $e.attr('data-content')
  37. || (typeof o.content == 'function' ?
  38. o.content.call($e[0]) :
  39. o.content)
  40. }
  41. // 重载tooltip里的arrow方法:获取显示的小箭头
  42. Popover.prototype.arrow = function () {
  43. return this.$arrow = this.$arrow || this.tip().find('.arrow')
  44. }
  45. // 重载tooltip里的tip方法:获取模板内容
  46. Popover.prototype.tip = function () {
  47. if (!this.$tip) this.$tip = $(this.options.template)
  48. // 如果$tip不存在,则用options里的template模板
  49. return this.$tip
  50. }

步骤3 jQuery插件定义。在jQuery上定义$.fn.tpopoverab插件,没有什么特殊的地方。代码如下:

  1. var old = $.fn.popover
  2. // 保留其他库的$.fn.popover代码(如果定义的话),以便在noConflict之后,可以继续使用该老代码
  3. $.fn.popover = function (option) {
  4. return this.each(function () { // 根据选择器,遍历所有符合规则的元素
  5. var $this = $(this) // this赋值一个变量,防止作用域改变
  6. var data = $this.data('bs.popover') // 查询当前元素上是否已经有了popover实例
  7. // 如果option是对象,说明是参数集合,在new popover的时候传入
  8. var options = typeof option == 'object' && option
  9.  
  10. if (!data && option == 'destroy') return
  11.  
  12. // 如果没有popover实例,就初始化一个,并传入this
  13. if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
  14.  
  15. // 如果option是字符串,说明传入的是一个方法,直接调用该方法
  16. if (typeof option == 'string') data[option]()
  17. });
  18. }
  19. $.fn.popover.Constructor = Popover // 重设插件构造器,可以通过该属性获取插件的真实类函数

步骤4 防冲突处理。此步骤与Modal插件的步骤4一样,此处不赘述。

步骤5 绑定触发事件。与提示框插件一样,由于性能问题,没有定义绑定触发事件的代码。