此组件可以将一篇区域收起、展开,类似折叠的效果。主要用于内容过多时,将部分内容折叠。
.collapse {
display: none;
&.show {
display: block;
}
}
.collapsing {
position: relative;
height: 0;
overflow: hidden;
@include transition($transition-collapse);
}
而card类貌似是取代了之前的panel,所以这里要使用card类包裹折叠元素,才能实现多个同类元素折叠相互影响的效果。
作用
代码梗概
class Collapse {
// 构造函数
constructor(element, config) {
// 正在 collapsing
this._isTransitioning = false
this._element = element
this._config = this._getConfig(config)
// 触发这个元素collapse的按钮
this._triggerArray = $.makeArray($(
`[data-toggle="collapse"][href="#${element.id}"],` +
`[data-toggle="collapse"][data-target="#${element.id}"]`
))
// 如果元素有 data-parent属性,表示有多个此类组件需要实现相互影响的折叠效果
// 这里除了获取了parent,还为同属于一个parent的多个同类组件按钮以及元素设置属性(包括自己)
// 同个parent下的被折叠元素,如果当前状态是展开,那么会被设置属性aria-expanded为true, 同时,其控制按钮的collapsed类会被移除(如果有的话)且aria-expanded属性也被设置为true;情况相反则以此类推(注意,collapsed类无属性)
this._parent = this._config.parent ? this._getParent() : null
// 如果只是单个组件活动,那么也为自己设置上述属性
if (!this._config.parent) {
this._addAriaAndCollapsedClass(this._element, this._triggerArray)
}
// 初始化的同时对元素折叠或是展开
// $('#myCollapsible').collapse({toggle: false})激活元素为collapse组件的同时不进行折叠或是展开
if (this._config.toggle) {
this.toggle()
}
}
// 展开
show() {}
// 折叠
hide() {}
// 外部接口
static _jQueryInterface(config) {}
}
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
event.preventDefault()
// 实例创建在被按钮控制的元素上
const target = Collapse._getTargetFromElement(this)
const data = $(target).data(DATA_KEY)
// 如果已经实例化过,那么相当于调用$('xx').collapse('toggle')
// 否则,使用jquery data的属性实例化
const config = data ? 'toggle' : $(this).data()
Collapse._jQueryInterface.call($(target), config)
})
代码中的注释有提到,实例创建在被控制的元素上,且实例一旦创建,只要不被销毁,实例就不会变化;由此可知,如果不是人为调用 $(xxx).data(DATA_KEY)
改变元素属性,实例配置一般就不变了。所以,如果使用js初始化一个实例,如果不传parent属性,即使在控制按钮上声明属性 data-parent="xxx"
也是相当于没有parent属性的。
主要也就是两个函数,其实还有个 toggle
函数,这里代码没贴出来,不过此函数就是通过判断元素是否有css show
类来调用 show
或是 hide
函数,在这里没有必要介绍。
function show
此函数作用是让元素展开,如果存在与其他同类组件,且通过 data-parent
需要相互影响,那么会在打开此元素的同时关闭其他元素
show() {
let actives
let activesData
// 如果有需要相互影响的元素,那么把他们找出来
if (this._parent) {
// 找寻同一个parent下的,所有已经展开或正在进行动画的元素(不是按钮)
// Selector.ACTIVES === '.card > .show, .card > .collapsing'
// 这里值得提一下,在之前的bootstrap版本中,我发现想要多个元素相互影响,需要具有 `.panel` 类,而在这个版本中,要的是 `card` 类,这是必须要的
actives = $.makeArray($(this._parent).find(Selector.ACTIVES))
if (!actives.length) {
actives = null
}
}
// 触发事件钩子,同其他组件一样,可以在此时阻止此函数继续执行
const startEvent = $.Event(Event.SHOW)
$(this._element).trigger(startEvent)
if (startEvent.isDefaultPrevented()) {
return
}
if (actives) {
// 关闭已经打开的元素
Collapse._jQueryInterface.call($(actives), 'hide')
// 如果原来此元素是没有Collapse实例在上面的,那么上一步生成的实例将被剔除
// 我猜是因为实例生成时没有读取配置的过程吧?
if (!activesData) {
$(actives).data(DATA_KEY, null)
}
}
// collapse类只有一个功能: display: none
// collapsing 则用于让元素具备动画属性
$(this._element)
.removeClass(ClassName.COLLAPSE)
.addClass(ClassName.COLLAPSING)
// 让高度为0(dimension === 'height')
this._element.style[dimension] = 0
this._element.setAttribute('aria-expanded', true)
// 为这个元素的按钮移除collapsed类(类无属性),标识对应元素已不再是collapsed状态
if (this._triggerArray.length) {
$(this._triggerArray)
.removeClass(ClassName.COLLAPSED)
.attr('aria-expanded', true)
}
// 动画开始
this.setTransitioning(true)
// 监听动画结束事件,这里不再分析
// 目前有了 collapsing类,即有了动画属性,这里设置 元素的 height = style.scrollHeight
// 设置动画结束后的钩子,内容是移除collapsing类,添加collapse和show类,两个类在一起时候只有一个作用: display: block
// 触发用户自定义事件钩子
this._element.style[dimension] = `${this._element[scrollSize]}px`
}
从这里可以发现,想要实现多个此类组件相互影响的效果,必须为每个被折叠元素的外层套一个带有类 .card
的包装,这样才能被js捕捉到,如下:
<div class="part card">
<h1><a href="#content-1" data-toggle='collapse' data-parent='#accordin'>
Toggle the Content-1
a>h1>
<article id="content-1" class="collapse show">
...
article>
div>
<div class="part card">
...
div>
<div class="part card">
...
div>
function hide
此函数相对于 show
函数要简单些,因为一个元素的关闭并不需要影响其他元素
hide() {
// 触发自定义事件钩子
const startEvent = $.Event(Event.HIDE)
$(this._element).trigger(startEvent)
if (startEvent.isDefaultPrevented()) {
return
}
// 让元素: style.height = style.offsetHeight
this._element.style[dimension] = `${this._element[offsetDimension]}px`
// 强制页面回流,避免js的
Util.reflow(this._element)
// 通过collapsing类添加动画效果
$(this._element)
.addClass(ClassName.COLLAPSING)
.removeClass(ClassName.COLLAPSE)
.removeClass(ClassName.SHOW)
this.setTransitioning(true)
// 设置height = 0 ,且注册动画结束钩子
this._element.style[dimension] = ''