bootstrap源码学习与示例:bootstrap-button

今天开始说非常简单的bootstrap-button组件。

button设计得非常smart,只有一个调用接口,你可以在$().button(str)中传入不同的字符串,来切换它的文本,其中有三个是框架占用的——toggle, reset, loading。

button的文本切换是通过data-xxx-text自定义属性控制。$().button("xxx"),就是取data-aaa-text的值作为新文本。如果要还原最初状态,使用$().button("reset")。loading参数会在这基础上添加disabled类名与disabled属性,目的是不让人家修改。

button的类型是通过data-toggle自定义属性决定,目前有三种。普通的单个按钮,checked类型的按钮组,radio类型的按钮组,分别对应button, buttons-checked, buttons-radio。

要使用bootstrap的样式,必须加btn类名。皮肤在通用样式名前加个btn-前缀,即.btn-primary, .btn-warning, .btn-danger, .btn-success, .btn-info, .btn-inverse

除了切换文本需要你动手外,切换状态的点击事件组件已经为你做好了,你只要引入它就能用了。

!function ($) {

    "use strict"; // jshint ;_;

    /* BUTTON PUBLIC CLASS DEFINITION
  * ============================== */
    var Button = function (element, options) {
        this.$element = $(element)
        this.options = $.extend({}, $.fn.button.defaults, options)
    }

    Button.prototype.setState = function (state) {
        var d = 'disabled'
        , $el = this.$element
        , data = $el.data()
        , val = $el.is('input') ? 'val' : 'html'//如果是button标签,使用html方法

        state = state + 'Text'
        //如果是reset,则变成resetText保留起来,换言之reset对框架而言是个保留字
        data.resetText || $el.data('resetText', $el[val]())
        //切换文本
        $el[val](data[state] || this.options[state])
        //如果是loading,那么它就添加一个disabled类名与disabled属性,换言之reset对框架而言是个保留字
        setTimeout(function () {
            state == 'loadingText' ?
            $el.addClass(d).attr(d, d) :
            $el.removeClass(d).removeAttr(d)
        }, 0)
    }
    //这个用于按钮组,通过$().button('toggle')调用
    Button.prototype.toggle = function () {
        var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
        //radio具有排他性,只有一个按钮组只有一个按钮存在激活状态
        $parent && $parent
        .find('.active')
        .removeClass('active')
      
        this.$element.toggleClass('active')
    }


    var old = $.fn.button

    /* BUTTON PLUGIN DEFINITION
  * ======================== */
    $.fn.button = function (option) {
        return this.each(function () {
            var $this = $(this)
            , data = $this.data('button')
            , options = typeof option == 'object' && option
            //重复利用之前的实例
            if (!data) $this.data('button', (data = new Button(this, options)))
            if (option == 'toggle') data.toggle()
            else if (option) data.setState(option)
        })
    }

    $.fn.button.defaults = {
        loadingText: 'loading...'
    }

    $.fn.button.Constructor = Button

    /* BUTTON NO CONFLICT
  * ================== */
    $.fn.button.noConflict = function () {
        $.fn.button = old
        return this
    }

    /* BUTTON DATA-API
  * =============== */
 //为存在data-toggle属性,并且其值以button开头的按钮绑定点击事件
    $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) {
        var $btn = $(e.target)
        if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
        $btn.button('toggle')
    })

}(window.jQuery);

以下是它对应的less,生成的CSS大概是它的五倍以上。

//
// Buttons
// --------------------------------------------------


// Base styles
// --------------------------------------------------

// Core
.btn {
  display: inline-block;
  .ie7-inline-block();
  padding: 4px 12px;
  margin-bottom: 0; // For input.btn
  font-size: @baseFontSize;
  line-height: @baseLineHeight;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75));
  border: 1px solid @btnBorder;
  *border: 0; // Remove the border to prevent IE7's black border on input:focus
  border-bottom-color: darken(@btnBorder, 10%);
  .border-radius(@baseBorderRadius);
  .ie7-restore-left-whitespace(); // Give IE7 some love
  .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)");

  // Hover state
  &:hover {
    color: @grayDark;
    text-decoration: none;
    background-position: 0 -15px;

    // transition is only when going to hover, otherwise the background
    // behind the gradient (there for IE