bootstrap外掛學習-bootstrap.modal.js

風靈使發表於2018-12-31

先從bootstrap.modal.js的結構看起。

function($){
        var Modal = function(){}  //構造器
        Modal.prototype = function(){} //構造器的原型
        function ..(){}  //自定義方法 
        $.fn.modal = function(){} //在jQuery物件上自定方法
        $.fn.modal.defaults = {} //設定預設屬性
        $.fn.modal.Constructor = Modal //重置構造器名
        $(function(){})   //初始化
    }(window.jQuery)

其HTML結構

<a class="btn" data-toggle="modal" href="#myModal" >點選觸發對話方塊</a>

<div class="modal" id="myModal">
      <div class="modal-header">
        <a class="close" data-dismiss="modal">×</a>
        <h3>對話方塊標題</h3>
      </div>
      <div class="modal-body">
        <p>對話方塊內容</p>
      </div>
      <div class="modal-footer">
      <a href="#" class="btn" data-dismiss="modal">關閉</a>
      <a href="#" class="btn btn-primary" data-dismiss="modal">儲存更新</a>
  </div>
</div>

我們開始過一遍外掛原始碼:從初始化開始

$(function () {
     /*
     * 在所有有data-toggle='modal'屬性的標籤上幫上click事件,一般為a標籤
     * /.*(?=#[^\s]+$)/表示如果匹配成功將#之前的資訊刪除
     * 對於jQuery物件,比如a標籤有data-xx型別的,在其data方法中都可以顯示
     * 將a標籤data-target指向的或是href的錨點指向的元素傳入modal方法裡
     * */
    $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
      var $this = $(this), href
        , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
        , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data());
      e.preventDefault()
      $target.modal(option)
    })
  })

根據HTML提供的結構,$targeta標籤所指向的彈出框divJquery物件。另外$this.data()存在資料,為{toggle:'modal'},這個我在註釋中已經解釋。最後執行$target.modal(option),我們進入jQuery物件的這個方法。

$.fn.modal = function ( option ) {
    return this.each(function () {
      var $this = $(this)
        , data = $this.data('modal')
        , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
      if (!data) $this.data('modal', (data = new Modal(this, options)))//為modal繫結物件
      if (typeof option == 'string') data[option]()
      else if (options.show) data.show()
    })
  }

初始化一些引數,在彈出框div上加入了一個modal屬性,如果這個modal屬性不存在,則例項化Modal構造器。最後執行show方法,因為此時你已經點選了a標籤,讓div顯示。程式碼中有if(typeof option == 'string')data[option],表示程式碼支援傳入方法名,執行該方法,在執行show方法之前,我們先進入Modal的構造器中看看。

/*
  * 構造器,為所有data-dismiss='modal'屬性的標籤繫結原型上的hide方法
  * */
  var Modal = function ( content, options ) {
    this.options = options
    this.$element = $(content)
      .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
  }

很簡單,讓帶有data-dismiss='modal'屬性的標籤的click事件交給彈出框div代理,注意這裡的執行的方法。這裡為什麼要$.proxy(this.hide,this)這樣寫,第一,需要明白此時的this表示著構造器的例項,代理的事件需要使用到例項的hide方法,為什麼例項擁有hide方法,很簡單thisModal的例項,它繼承了Modal原型的方法。那proxy方法中的第二個this,則表示著這個hide方法的呼叫者,即上下文,可以試想一下,如果我們不修改這個,那執行這個方法時,其裡面的this是不是指向了呼叫它的a標籤呢?(注意留意HTML結構,擁有data-dismiss屬性的三個a標籤,其實分別就是彈出框的關閉按鈕等),那原來定義在原型上的hide方法中的this的屬性將不能在正常使用。
綜上,例項化,主要就是給彈出框繫結了hide事件,為將來關閉彈出框,做準備。

好,回到之前JQuery的modal方法上,最後我們執行了show方法,進入show方法。

下面是執行的流程圖,就是各個方法的執行順序

show -> escape -> backdrop
hide ->escape -> hideModal -> backdrop

show

    , show: function () {
        var that = this

        if (this.isShown) return

        $('body').addClass('modal-open')//給body加上modal-open類

        this.isShown = true//將isShown屬性設定成true

        this.$element.trigger('show');//沒理解

        escape.call(this)//加上鍵盤事件

        backdrop.call(this, function () {
          var transition = $.support.transition && that.$element.hasClass('fade')

          //console.log(that.$element);
          /*
          * 插入彈出層
          * */
          !that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position

          that.$element
            .show()//jQuery方法顯示

          if (transition) {
            that.$element[0].offsetWidth // force reflow
          }

          that.$element.addClass('in')

          transition ?
            that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
            that.$element.trigger('shown')

        })
      }

談到showhide的時候,我們需要留意它們原始碼中的一些我們認為的’廢’操作,其實它能幫助我們實現擴充套件。比如給bodymodal-open這個類,如果你要在開啟彈出層,執行其他程式,可以通過判斷這個類來實現,這是bootstrap它自身已經提供的標識。escape()方法是一個新增鍵盤事件和刪除鍵盤事件的方法,它可以讓你按enter鍵也能關閉彈出框,比較簡單。

接下來我們進入backdrop方法

/*
  * 生成遮罩層,並且控制遮罩層的顯示或刪除
  * */
  function backdrop( callback ) {
          var that = this
      , animate = this.$element.hasClass('fade') ? 'fade' : ''

    if (this.isShown && this.options.backdrop) {
      var doAnimate = $.support.transition && animate
      //console.log(doAnimate);
      this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
        .appendTo(document.body)//遮罩層,讓彈出框顯示時,背景被遮罩,此時為透明的

      if (this.options.backdrop != 'static') {
        this.$backdrop.click($.proxy(this.hide, this))
      }

      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow

      this.$backdrop.addClass('in')//加入in類,顯示遮罩層

      doAnimate ?
        this.$backdrop.one($.support.transition.end, callback) :
        callback()
      //開啟時,執行callback()回撥
    } else if (!this.isShown && this.$backdrop) {
      this.$backdrop.removeClass('in')//刪除in,遮罩層透明
      $.support.transition && this.$element.hasClass('fade')?
        this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
        removeBackdrop.call(this)
        //一般執行removeBackdrop()函式
    } else if (callback) {
      callback()
    }
  }

這裡為什麼要判斷彈出框是否有fade類,在bootstrap.css中,.fade擁有left:-25%的屬性,當.fade .in時則left變為10%,在版本較高的瀏覽器中,有自上向下的移動的效果(低版本瀏覽器沒有效果,不過這個可以jQuery實現…),這個部分需要我們結合css樣式看,注意in的樣式,就可以了,彈出框顯示的最後一步,執行callback回撥,顯示彈出框,並給彈出框加上in類,80%透明度。最後完成顯示。

之前因為我們已經在彈出框的三個a標籤上都繫結了hide事件,所以當我們點選關閉時,執行hide方法

hide: function ( e ) {
        e && e.preventDefault()

        if (!this.isShown) return

        var that = this
        this.isShown = false

        $('body').removeClass('modal-open')

        escape.call(this)//關閉按鍵事件

        this.$element
          .trigger('hide')
          .removeClass('in')//jQuery方法,將彈出框隱藏,並將div的class去in

        $.support.transition && this.$element.hasClass('fade') ?
          hideWithTransition.call(this) :
          hideModal.call(this)
      }

其實跟show方法差不多,主要是給show擦屁股,進入hideModal方法

function hideModal( that ) {
    this.$element
      .hide() //刪除效果更好
      .trigger('hidden')

    backdrop.call(this)
  }

蠻蛋疼的方法,還是呼叫backdrop。最後刪除時我們呼叫的是removeBackdrop方法。

/*
  * 清除遮罩層
  * */
  function removeBackdrop() {
    this.$backdrop.remove()
    this.$backdrop = null
  }

將之前建立的遮罩層刪除。以上完成一遍彈出框的開啟顯示。

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

相關文章