思路
- 获取所有的菜单,点击时展开或者折叠其兄弟元素的菜单
- 给子菜单设置一个属性status,判断其状态是关闭,打开,还是正在进行中
- 根据其属性写打开子菜单的函数,关闭子菜单的函数,还有两者之间相互切换
- 封装一个展示动画效果的函数

<ul class="container">
<li class="menu">
<h2>菜单1</h2>
<ul class="submenu">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
</ul>
</li>
<li class="menu">
<h2>菜单2</h2>
<ul class="submenu">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
</ul>
</li>
<li class="menu">
<h2>菜单3</h2>
<ul class="submenu">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
</ul>
</li>
<li class="menu">
<h2>菜单4</h2>
<ul class="submenu">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
</ul>
</li>
</ul>
var title = document.querySelectorAll(".menu h2")
var itemHeight = 30
var totalMS = 300
for (var i = 0; i < title.length; i++){
title[i].onclick = function () {
var beforeOpen = document.querySelector('.submenu[status=open]')
if (beforeOpen) {
closeSubMenu(beforeOpen)
}
toggleSubMenu(this.nextElementSibling)
}
}
function openSubMenu(subMenu) {
var status = subMenu.getAttribute('status')
if (status && status !== 'close') {
return
}
subMenu.setAttribute('status', 'playing')
createAnimation({
from: 0,
to: itemHeight * subMenu.children.length,
totalMS: totalMS,
move(n) {
subMenu.style.height = n + 'px'
},
moveEnd() {
subMenu.setAttribute('status', 'open')
}
})
}
function closeSubMenu(subMenu) {
var status = subMenu.getAttribute('status')
if (status !== 'open') {
return
}
subMenu.setAttribute('status', 'close')
createAnimation({
from: itemHeight * subMenu.children.length,
to: 0,
totalMS: totalMS,
move(n) {
subMenu.style.height = n + 'px'
},
moveEnd() {
subMenu.setAttribute('status', 'close')
}
})
}
function toggleSubMenu(subMenu) {
var status = subMenu.getAttribute('status')
if (status === 'playing') {
return
} else if (status === 'open') {
closeSubMenu(subMenu)
} else {
openSubMenu(subMenu)
}
}
function createAnimation(option) {
var from = option.from
var to = option.to
var totalMS = option.totalMS || 500
var duration = option.duration || 15
var time = totalMS / duration
var dis = Math.floor(to - from) / time
var currentTime = 0
var timerId = setInterval(() => {
from += dis
currentTime++
if (currentTime >= time) {
from = to
clearInterval(timerId)
option.moveEnd && option.moveEnd()
}
option.move && option.move(from)
}, duration);
}