bootstrap插件學習-bootstrap.tooltip.js


先看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對象點就能點出上述的方法了,很方便。

 內容不多,時間剛好,以上是我的一點讀碼體會,如有錯誤,請指出,大家共通學習。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM