bootstrap源碼jQuery插件之button.js源碼分析筆記

架不住移動端的兼容的誘惑,bootstrap隨着學習,它的強大以及可以讓我學習的地方衆多。今天又來寫寫button.js這個相對簡單的按鈕插件,寫完這篇文章,估計我對js又有新的提高和鞏固。本文只是作爲本人筆記,看法,不保證對,也很膚淺,大神繞道。

個人認爲知識點:jQuery插件:能夠進一步學會如何封裝某個js庫的插件,能夠學習更多平時不常用卻比較重要的js語法基礎

bootstrap的js源碼:能夠更好的理解bootstrap框架動態效果展示

bootstrap的css3和js的配合使用:它大量使用css3動畫效果,transition,transform用的666


雖然bootstrap使用頻率很高,也相對好用,但還是非常多人反對直接使用它,理由各式各樣,但無可否認,它是最受歡迎的前端樣式框架,既然大受歡迎,對於我這種菜鳥來說,還是值得好好研讀。研究它的使用,研究它的源碼,研究框架分工,各個分工的實現,less代碼編譯工具使用等等。

迴歸正題,buttonjs源碼分析筆記開始。

首先jQuery插件通用寫法,匿名立即執行函數,傳入實參jQuery,形參$。這給我們一個啓示,製作某個js庫的依賴插件庫,都是要傳入那個js庫的參數;由於js語言的特性,爲了避免變量污染,都需要立即執行函數隔離變量,需要暴露的變量註冊爲全局變量。

+function($){
//........
}(jQuery);

嚴格模式:最直接的目的是逐步淘汰掉以前js語法中的糟粕;消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行爲;消除代碼運行的一些不安全之處,保證代碼運行的安全;提高編譯器效率,增加運行速度;爲未來新版本的Javascript做好鋪墊。隨着es6標準的逐步落地實現,隨着各個大項目,開源框架vue、 angular等使用es6,養成使用嚴格模式習慣,是非常重要的。

'use strict';

使用嚴格模式,會禁止使用某些關鍵字,變量必須事先var 聲明等等,在某些程度上改變js原來的宗旨:寬鬆,動態,腳本。時過境遷,畢竟以前js承擔任務不多,現在瀏覽器發展,前端作用越來越強,js做出限制以及增加是必須的,不過感覺越來越向強類型語言靠攏的感覺。

//button類構造函數
  var Button = function (element, options) {
    this.$element  = $(element)
    this.options   = $.extend({}, Button.DEFAULTS, options) //jQuery的擴展方法,插件必備:將options合併到Button.DEFAULTS中
    this.isLoading = false //是否加載
  }

$.extend是我們在寫插件的過程中常用的方法,該方法有一些重載原型extend(dest,src1,src2,src3...) 

它的含義是將src1,src2,src3...合併到dest中,返回值爲合併後的dest,由此可以看出該方法合併後,是修改了dest的結構的。如果想要得到合併的結果卻又不想修改dest的結構,

可以如下使用:

var aa=$.extend({},{x:"x",y:'y'},{x:"x2",z:"z"})

結果展示

aa={x:'x2',y:'y',z:'z'}
也即是源碼中的樣子

this.options   = $.extend({}, Button.DEFAULTS, options) 
button的loading狀態,通過獲取button當中的value值或者html值,然後在loading時候將button設置爲disabled=true,禁止點擊等待下載,下載完畢允許點擊
//設置button狀態
  Button.prototype.setState = function (state) {
    var d    = 'disabled'  //禁用
    var $el  = this.$element
    var val  = $el.is('input') ? 'val' : 'html' //如果是input控件,存取input內容,否則存取html內容
    var data = $el.data()//data() 方法向被選元素附加數據,或者從被選元素獲取數據。

    state += 'Text' //拼接Text字符串,便於理解字面意思

    if (data.resetText == null) $el.data('resetText', $el[val]())//如果data沒有resetText綁定,則創建

    // push to event loop to allow forms to submit
    //$.proxy()該方法通常用於向上下文指向不同對象的元素添加事件。保證了this指向,避免作用域改變而導致錯誤
    setTimeout($.proxy(function () {
      $el[val](data[state] == null ? this.options[state] : data[state])

      if (state == 'loadingText') {
        this.isLoading = true
        $el.addClass(d).attr(d, d).prop(d, true)//添加class="disabled" disabled="true"
      } else if (this.isLoading) {
        this.isLoading = false
        $el.removeClass(d).removeAttr(d).prop(d, false)//移除上面添加的屬性
      }
    }, this), 0)
  }

button切換,例如radio checkbox 和其它類型的input,把單選框和複選框分別區分,其它的都是輸入框,判斷完成

//按鈕效果切換,radio checkbox 
  Button.prototype.toggle = function () {
    var changed = true
    var $parent = this.$element.closest('[data-toggle="buttons"]')//closest表示從this.$element開始檢索擁有data-toggle="buttons"屬性的標籤
    //區分radio checkbox按鈕,active是規定好的類樣式
    if ($parent.length) {
      var $input = this.$element.find('input')
      if ($input.prop('type') == 'radio') {
        if ($input.prop('checked')) changed = false
        $parent.find('.active').removeClass('active')
        this.$element.addClass('active')
      } else if ($input.prop('type') == 'checkbox') {
        if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
        this.$element.toggleClass('active')
      }
      $input.prop('checked', this.$element.hasClass('active'))
      if (changed) $input.trigger('change')
    } else {
      this.$element.attr('aria-pressed', !this.$element.hasClass('active'))//arai盲人無障礙閱讀屬性
      this.$element.toggleClass('active')
    }
  }

bootstrap的button插件自定義:

① 暴露類名, 可以通過這個爲插件做自定義擴展

// 擴展的方式
// 設置 : $.fn.button.Constructor.newMethod = function(){}
// 使用 : $btn.button("newMethod");

② 無衝突處理

③ 事件代理, 智能初始化

以上是開發自定義插件必要過程,由於沒什麼經驗,具體不做分析,百度去吧

 // BUTTON PLUGIN DEFINITION
  // ========================

  function Plugin(option) {
    return this.each(function () {
      var $this   = $(this) //保存this作用域
      // 判斷是否初始化過的依據
      var data    = $this.data('bs.button') //data() 方法向被選元素附加數據,或者從被選元素獲取數據。
      var options = typeof option == 'object' && option //如果option爲object類型和option參數值,返回true,否則返回false

      if (!data) $this.data('bs.button', (data = new Button(this, options)))// 如果沒有初始化過, 就初始化它,爲元素附加Button實例

      if (option == 'toggle') data.toggle()
      else if (option) data.setState(option)
    })
  }

  
  var old = $.fn.button 

  $.fn.button             = Plugin
  // ① 暴露類名, 可以通過這個爲插件做自定義擴展
  $.fn.button.Constructor = Button


  // BUTTON NO CONFLICT
  // ==================
  //② 無衝突處理
  $.fn.button.noConflict = function () {
    $.fn.button = old
    return this
  }


  // BUTTON DATA-API
  // ===============
  // ③ 事件代理, 智能初始化
  $(document)
    .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
      var $btn = $(e.target).closest('.btn')
      Plugin.call($btn, 'toggle')
      if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
        // Prevent double click on radios, and the double selections (so cancellation) on checkboxes
        e.preventDefault()
        // The target component still receive the focus
        if ($btn.is('input,button')) $btn.trigger('focus')
        else $btn.find('input:visible,button:visible').first().trigger('focus')
      }
    })
    .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
      $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
    })

雖然沒有完全看懂,但也學到jQuery一些API的使用,知道bootstrap的button控制流程,下次繼續研究,加油


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章