Bootstrap源码分析

bootstrap是twitter公司基于HTML5、CSS3和jQuery创建的一种前端框架,是现在使用范围比较广泛的一种前端框架,其源码对于学习前端的一些技术非常有帮助。

bootstrap源码可以从bootstrap中文网(http://www.bootcss.com/)上面下载,但是安装源码需要配置Nodejs环境,安装方法可以自行百度。这个网站上面也可以下载到用于生产的精简源码。

bootstrap源码主要有几个部分,包括样式表,脚本以及字体库等等,其中最重要的就是脚本,所以这里只拿脚本来进行分析。

jQuery版本检测

+function ($) {  
  //+function中的+号仅仅表示添加的意思,并没有实际效果
  'use strict';  
  //use strict放在函数的开头表示函数咋在严格模式下运行
  var version = $.fn.jquery.split(' ')[0].split('.')
  ...... //版本检测
  }
}(jQuery);

bootstrap源代码中bootstrap.js文件中的所有插件都是这样的结构,首先插件是以一个直接运行的匿名函数构造的,这个匿名函数的形参是$,实参是jQuery对象,并且在function关键前面有一个+号,这个加号并没有实际的含义,这个符号仅仅表示添加一个函数。所有的bootstrap插件都是在严格模式下运行的,以标记”use strict”来表示出来,并且bootstrap插件使用的是jQuery进行编写的,所以首先要检测jQuery的版本是否足够,我现在使用的是3.3.5版本的bootstrap,这个版本要求的jQuery的最低版本是1.9.1。

CSS过渡浏览器支持检测

bootstrap中的基本所有的动画都是使用CSS过渡来完成的,并没有使用jQuery自带的动画函数来完成。所以需要检测浏览器内核版本来确定CSS中使用transition的前缀。transitionEnd事件是在CSS过渡完成之后自动触发的一个事件,有些动画需要在动画结束之后对执行动画的元素进行一些收尾工作,包括数据清理等,所以需要一个可以触发的事件,用来给他绑定回调函数。

具体和CSS过渡有关的内容可以点这里:CSS过渡&&bootstrap transition

+function ($) {

//检测浏览器支持的CSS transition属性名。和浏览器有关
  function transitionEnd() {
    var el = document.createElement('bootstrap')

    var transEndEventNames = {
      WebkitTransition : 'webkitTransitionEnd',
      MozTransition    : 'transitionend',
      OTransition      : 'oTransitionEnd otransitionend',
      transition       : 'transitionend'
    }

    for (var name in transEndEventNames) {
      if (el.style[name] !== undefined) {
        return { end: transEndEventNames[name] }
      }
    }

    return false 
  }

// 扩展jQuery.fn对象,添加实例化方法emulateTransitionEnd,这个方法是保证如果
// transitionEnd事件没有被正确触发,那么会自动在duration时间段后进行回调
  $.fn.emulateTransitionEnd = function (duration) {
    .......
    setTimeout(callback, duration)
    return this
  }

  $(function () {
//增加一个support变量transition,其值为当前浏览器适应的transition版本
    $.support.transition = transitionEnd()
  })

}(jQuery);

Alert类分析

bootstrap插件的基本所有插件的代码风格都十分相似,基本的函数,冲突检测,插件注册方法基本都是一样,这里用一段比较短的Alert类进行分析吧(因为实在是太懒了,第二短的button都要几百行代码lol)。

+function ($) {
  //省略了源码中部分的变量声明和初始化。如果想看完整代码要去bootstrap中文网自己安装哦
  var dismiss = '[data-dismiss="alert"]'
  var Alert   = function (el) {
    $(el).on('click', dismiss, this.close)
  }  //也就是给el元素中的第一个具有符合dismiss的元素添加onclick的事件处理函数
  Alert.prototype.close = function (e) {
    var $this    = $(this)
    var selector = $this.attr('data-target')
    //寻找data-target指定的关闭对象,如果没有指定关闭对象,则从href中获取,如果href中仍
    //没有关闭对象,那就从叉号的祖先元素中获取类为alert的对象进行关闭
    if (!selector) {
      selector = $this.attr('href')
      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
    }

    var $parent = $(selector)

    if (e) e.preventDefault()
    //解除点击事件的默认事件处理函数

    if (!$parent.length) {
      $parent = $this.closest('.alert')
      //寻找x按钮的祖先元素中的class=alert的元素,也就是需要关闭的元素
    }

    $parent.trigger(e = $.Event('close.bs.alert'))
    //新建一个事件close.bs.alert,并且用这个事件来触发上面选中的parent元素

    if (e.isDefaultPrevented()) return

    $parent.removeClass('in')

    function removeElement() {
      // detach from parent, fire event then clean up data
      $parent.detach().trigger('closed.bs.alert').remove()
      //移除选中的祖先节点并且取消其事件,清理数据内容
    }

    $.support.transition && $parent.hasClass('fade') ?
      $parent
        .one('bsTransitionEnd', removeElement)  //在transition动画完毕之后,调用一次removeElement函数
        .emulateTransitionEnd(Alert.TRANSITION_DURATION) :  //emulateTransitionEnd是防止TransitionEnd不发生而设置的定时函数
      removeElement()
    //检测支持的transition动画版本,如果存在则使用动画渐隐元素
  }
}(jQuery);

上面这段代码片是bootstrap用来关闭alert的x号的,首先为x号注册鼠标单击的事件处理函数。之后完成这整个事件处理函数,事件处理函数中首先需要选择要关闭的对象,采用了三层选择方法,第一层首先选择x号本身指定的对象,对象名为x号里面的data-target属性,第二层使用x号内部href对应的对象名,第三层通过x按钮来寻找其祖先元素中的具有class=“alert”属性的元素来进行关闭。
bootstrap采用一个新建的事件名来触发关闭事件:

$parent.trigger(e = $.Event('close.bs.alert'))

之后通过之前的CSS过渡动画来移除元素的表现,在动画完成之后触发transitionEnd事件,用其回调函数来清理数据。如果没有自动触发transitionEnd事件,则调用emulateTransitionEnd方法来清理数据。

插件初始化方法:

  function Plugin(option) {
    //this指向jQuery对象,this.each遍历所有DOM元素,每个元素都调用function函数
    return this.each(function () {
      var $this = $(this)
      var data  = $this.data('bs.alert')
      //取得与bs.alert相关的数据项,如果没有数据项则新赋值一个数据项
      //data变量是用来检测插件是否初始化过的一句
      if (!data) $this.data('bs.alert', (data = new Alert(this)))
      if (typeof option == 'string') data[option].call($this)
    })
  }

插件的冲突检测方法:

  var old = $.fn.alert

  $.fn.alert             = Plugin
  $.fn.alert.Constructor = Alert


  //冲突检测,假设有原本的其他插件的alert方法,那么将原本的alert方法
  //赋值给一个临时变量old,然后在调用$.fn.alert.noConfict方法的时候,
  //这个方法将原本的alert方法old还原到alert方法上,并且返回bootstrap版本
  //的alert方法
  $.fn.alert.noConflict = function () {
    $.fn.alert = old
    return this
  }

这个冲突检测方法十分的精巧,既可以防止冲突,又可以安全的调用新的和旧的方法。
可以通过下面这个例子更好的了解bootstrap的冲突检测方法:

+function($){
    $.fn.alert = function(){
        alert("这是旧的alert方法");
    };
}(jQuery);

+function($) {
    var old = $.fn.alert;
    $.fn.alert = function(){
        alert("这是新的alert方法");
    };
    $.fn.alert.noConflict = function () {
        $.fn.alert = old;
        return this;
    }
}(jQuery);

$.fn.alert();  //这是新的alert方法

var newAlert = $.fn.alert.noConflict();
$.fn.alert();  //这是旧的alert方法
newAlert();    //这是新的alert方法

插件的智能注册方法:

$(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
//这段JavaScript代码将click委托事件监听器绑定在document元素上,
//并给click事件赋予命名空间click.bs.alert.data-api,
//选择器匹配的是属性data-dismiss的值为"alert"的标签。

基本上所有的bootstrap源码都是用这种结构写的插件,非常值得读一下,阅读好的代码是一个好的程序猿的必修课。。

你可能感兴趣的:(javaScript)