Bootstrap简单认识之Collapse组件

Collapse(内容折叠)组件

一、简介

此组件可以将一篇区域收起、展开,类似折叠的效果。主要用于内容过多时,将部分内容折叠。

二、样式

  1. 分析了这几个组件,发现Bootstrap喜欢将transition的持续时间设置为600ms
  2. 这个组件并没有什么特殊的样式,一共就几个:
.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] = ''

你可能感兴趣的:(前端)