手写手风琴-简易版

281 阅读1分钟
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      *{
        padding: 0;
        margin: 0;
        box-sizing: border-box;
      }
      dl{
        border-radius: 3px;
        width: 200px;
        color: #fff;
      }
      dt{
        height: 30px;
        background-color: #333;
      }
      dd{
        height: 100px;
        background-color: orange;
      }

    </style>
  </head>
  <body>
    <dl>
      <dt>title1</dt>
      <dd></dd>
      <dt>title2</dt>
      <dd></dd>
      <dt>title3</dt>
      <dd></dd>
    </dl>
    <script>
      // 动画类
      class Animation{
        constructor(el){
          this.el = el
          // 定时器间隔
          this.second = 20
          // 容器默认高度
          this.defaultHeight = this.height
        }
        get height(){
          return window.getComputedStyle(this.el).height.slice(0, -2) * 1
        }
        set height(val){
          this.el.style.height = val + 'px'
        }
        // 隐藏方法,每隔一定时间间隔高度减1像素
        hide(callback){
          let timer = setInterval(() => {
            if(this.height <= 0){ // 当高度小于等于0时,则清除定时器,调用回调函数
              clearInterval(timer)
              callback && callback()
              return
            }
            // 每间隔second毫秒,content的高度就减1
            this.height = this.height - 1
          }, this.second)
        }
        // 显示方法,每隔一定时间间隔高度增1像素
        show(callback){
          let timer = setInterval(() => {
            if(this.height >= this.defaultHeight){
              clearInterval(timer)
              callback && callback()
              return
            }
            this.height = this.height + 1
          }, this.second)
        }
      }

      // 面板内容类,继承了Animation类,也就有了Animation中的动画方法(显示、隐藏)
      class Content extends Animation{
        static count = 0
        // 根据参数隐藏需要隐藏的面板内容,参数是哪些面板内容,就隐藏哪些面板内容
        // items:是需要被隐藏的content集合
        static hideAll(items, callback){
          // 当连续点击title时,需要等前一次点击执行完再继续执行后面的一次点击
          if(Content.count > 0) return
          items.forEach((item) => {
            Content.count ++ // 执行一次自增一
            item.hide(() => {
              Content.count -- // 执行完一次自减一
            })
          })
          // 执行显示操作
          callback && callback()
        }
      }

      // 面板类(手风琴)
      class Accordion{
        // 构造函数,一般用于初始化属性和绑定事件处理函数
        constructor(wrap, title, content){
          // 面板容器
          this.wrap = document.querySelector(wrap)
          // 面板标题
          this.titles = this.wrap.querySelectorAll(title)
          // 面板内容,将所有面板的content映射为一个个的面板内容类,以便能使用Animation类中的动画
          this.contents = [...this.wrap.querySelectorAll(content)].map(item => new Content(item))
          // 事件绑定处理函数
          this.bindEvent()
        }
        // 事件绑定处理函数
        bindEvent(){
          let count = 0
          this.titles.forEach((title, i) => {
            title.addEventListener('click', () => {
              // 当每一个title被点击时,就隐藏其他面板内容,隐藏完之后,再显示与被点击title相同索引位置的面板内容
              // title与content是一一对应的,也就是索引一致
              Content.hideAll(this.filterContent(this.contents, i), this.contents[i].show.bind(this.contents[i]))
            })
          });
        }
        // 过滤出其他content,也就是过滤掉与所点击title一致索引的content
        filterContent(contents, i){
          return contents.filter((item, index) => index != i)
        }
      }

      // 实例化面板对象
      let accordion = new Accordion('dl', 'dt', 'dd')
    </script>
  </body>
</html>