bootstrap源码学习与示例:bootstrap-collapse - 司徒正美

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

这次说的是手风琴组件,但是bootstrap的实现有点奇怪,CSS中的类名以accordion为前缀,JS里面的处理组件与相关方法为collapse。

HTML结构以下:类名accordion的DIV包含N个类名为accordion-group的DIV,每组又分两部分accordion-heading与accordion-body。accordion-heading包含触发用的accordion-toggle,accordion-body里的accordion-inner才是用于放内容的。

       <div class="accordion" >
            <div class="accordion-group">
              <div class="accordion-heading">
                <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapseOne">
                  国土问题
                </a>
              </div>
              <div  class="accordion-body collapse" >
                <div class="accordion-inner">
                  前一段时间一个段子说,某国的网民在因国土问题与中国网民争吵时说,我要打到北京,中国的网民非常淡然地回应,就你那经济水平,交得起过路费吗?这两天新的段子说,李白要是活在今天的话,估计一大半以上他的诗根本写不出来,因为名山大川的门票他根本买不起。
                </div>
              </div>
            </div>
            <div class="accordion-group">
              <div class="accordion-heading">
                <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapseTwo">
                  门票问题
                </a>
              </div>
              <div  class="accordion-body collapse" >
                <div class="accordion-inner">
                  目前,中国半数5A级景区门票达到100元,黄山门票10年来由80元涨至230元。山东曲阜称,与同类景区相比收费较低,仅收150元,不涨票价就丢身价。曲阜的孔庙、孔府和孔林,年收入1.5亿元左右,全部上缴了地方财政,但景区维护成本从未公开。
                </div>
              </div>
            </div>
            <div class="accordion-group">
              <div class="accordion-heading">
                <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapseThree">
                  超生罚款
                </a>
              </div>
              <div  class="accordion-body in" >
                <div class="accordion-inner">
                  学者杨支柱因生二胎被取消公职,并罚款24万余元。他称,计生罚款以前直接叫超生罚款,入世后改成“社会抚养费”。根据9省市超生罚款的平均数,全国31个省市每年征收的超生罚款可高达279亿元。其中大城市将该收入上缴财政,而地方则分配混乱,部分罚款去向成谜。
                </div>
              </div>
            </div>
          </div>

与其他组件一样,只要引用它就能使用了。但绑定回调时,要绑定.accordion-body之上。感觉bootstrap的官方教程做得不太好,没有统一要绑定事件的类名,让人一看就知道绑在那里。不过要用户写HTML感觉不太好。

!function ($) {

    "use strict"; // jshint ;_;


    /* COLLAPSE PUBLIC CLASS DEFINITION
  * ================================ */

    var Collapse = function (element, options) {
        this.$element = $(element)//对应 accordion-body
        this.options = $.extend({}, $.fn.collapse.defaults, options)

        if (this.options.parent) {
            this.$parent = $(this.options.parent)
        }

        this.options.toggle && this.toggle()
    }

    Collapse.prototype = {

        constructor: Collapse

        ,
        dimension: function () {
            var hasWidth = this.$element.hasClass(\'width\')
            return hasWidth ? \'width\' : \'height\'
        }

        ,
        show: function () {
            var dimension
            , scroll
            , actives
            , hasData

            if (this.transitioning) return

            dimension = this.dimension()
            //如果没有指明width类名
            scroll = $.camelCase([\'scroll\', dimension].join(\'-\'))//求得scrollWidth 或scrollHeight
            //找到这个手风琴组件的所有展开的面板
            actives = this.$parent && this.$parent.find(\'> .accordion-group > .in\')
            //然后合上它们
            if (actives && actives.length) {
                hasData = actives.data(\'collapse\')
                if (hasData && hasData.transitioning) return
                actives.collapse(\'hide\')//合上它们,并削去它们的实例
                hasData || actives.data(\'collapse\', null)
            }
            //让当前面板的高度或宽度为零
            this.$element[dimension](0)
            //开始动画与触发事件
            this.transition(\'addClass\', $.Event(\'show\'), \'shown\')

            $.support.transition && this.$element[dimension](this.$element[0][scroll])
        }

        ,
        hide: function () {
            var dimension
            if (this.transitioning) return
            dimension = this.dimension()
            this.reset(this.$element[dimension]())
            this.transition(\'removeClass\', $.Event(\'hide\'), \'hidden\')
            this.$element[dimension](0)
        }

        ,
        reset: function (size) {
            var dimension = this.dimension()

            this.$element
            .removeClass(\'collapse\')
            [dimension](size || \'auto\')//还原为原来的大小
            [0].offsetWidth

            this.$element[size !== null ? \'addClass\' : \'removeClass\'](\'collapse\')

            return this
        }

        ,
        transition: function (method, startEvent, completeEvent) {
            var that = this
            , complete = function () {
                if (startEvent.type == \'show\') that.reset()
                that.transitioning = 0
                that.$element.trigger(completeEvent)
            }

            this.$element.trigger(startEvent)

            if (startEvent.isDefaultPrevented()) return

            this.transitioning = 1
            //添加或移除类名in
            this.$element[method](\'in\')

            $.support.transition && this.$element.hasClass(\'collapse\') ?
            this.$element.one($.support.transition.end, complete) :
            complete()
        }

        ,
        toggle: function () {//最重要的方法
            this[this.$element.hasClass(\'in\') ? \'hide\' : \'show\']()
        }

    }


    /* COLLAPSE PLUGIN DEFINITION
  * ========================== */

    var old = $.fn.collapse

    $.fn.collapse = function (option) {
        return this.each(function () {
            var $this = $(this)
            , data = $this.data(\'collapse\')
            , options = typeof option == \'object\' && option
            if (!data) $this.data(\'collapse\', (data = new Collapse(this, options)))
            if (typeof option == \'string\') data[option]()
        })
    }

    $.fn.collapse.defaults = {
        toggle: true
    }

    $.fn.collapse.Constructor = Collapse


    /* COLLAPSE NO CONFLICT
  * ==================== */

    $.fn.collapse.noConflict = function () {
        $.fn.collapse = old
        return this
    }


    /* COLLAPSE DATA-API
  * ================= */

    $(document).on(\'click.collapse.data-api\', \'[data-toggle=collapse]\', function (e) {
        var $this = $(this), href
        , target = $this.attr(\'data-target\') //取得它要展开或折叠的区域,1通过\'data-target\'
        || e.preventDefault() //2通过href
        || (href = $this.attr(\'href\')) && href.replace(/.*(?=#[^\s]+$)/, \'\') //strip for ie7
        , option = $(target).data(\'collapse\') ? \'toggle\' : $this.data()
        $this[$(target).hasClass(\'in\') ? \'addClass\' : \'removeClass\'](\'collapsed\')//?在CSS中没有看到此类名
        $(target).collapse(option)//初始化手风琴 bootstrap有个特点,都是点击时才开始初始化组件
    })

}(window.jQuery);

引入的less文件为accordion.less

bootstrap的手风琴只能以垂直形式出现。