什么是手风琴菜单?
- 多个一级菜单,每个一级菜单下有多个二级菜单
- 点击一级菜单,展开其二级菜单。再次点击,收起。
- 最多只能有一个一级菜单展开。
HTML部分
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>手风琴效果</title>
<link rel="stylesheet" href="/common/css/reset.css" />
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<ul class="container">
<li class="menu-item">
<h1 class="menu-title">菜单1</h1>
<ul class="submenu">
<li>菜单1-1</li>
<li>菜单1-2</li>
<li>菜单1-3</li>
<li>菜单1-4</li>
</ul>
</li>
<li class="menu-item">
<h1 class="menu-title">菜单2</h1>
<ul class="submenu">
<li>菜单2-1</li>
<li>菜单2-2</li>
<li>菜单2-3</li>
<li>菜单2-4</li>
</ul>
</li>
<li class="menu-item">
<h1 class="menu-title">菜单3</h1>
<ul class="submenu">
<li>菜单3-1</li>
<li>菜单3-2</li>
<li>菜单3-3</li>
<li>菜单3-4</li>
</ul>
</li>
<li class="menu-item">
<h1 class="menu-title">菜单4</h1>
<ul class="submenu">
<li>菜单4-1</li>
<li>菜单4-2</li>
<li>菜单4-3</li>
<li>菜单4-4</li>
</ul>
</li>
</ul>
<script src="/common/js/animate.js"></script>
<script src="./index.js"></script>
</body>
</html>
CSS部分
.container {
width: 200px;
margin: 0 auto;
margin-top: 100px;
}
.container .menu-item {
overflow: hidden;
background: rgb(50, 50, 50);
color: #fff;
line-height: 40px;
margin-bottom: 10px;
padding-left: 20px;
border-radius: 6px;
}
.container .menu-item .menu-title {
height: 40px;
line-height: 40px;
}
.container .menu-item .submenu {
height: 0;
}
.container .menu-item .submenu li {
border-radius: 1px;
font-size: 12px;
height: 30px;
line-height: 30px;
padding-left: 10px;
}
JS部分
index.js
(function () {
var menu_titles = document.querySelectorAll('.container .menu-item .menu-title')
console.log('menu_titles', menu_titles);
var submenus = document.querySelectorAll('.submenu')
var currentExpand = null
for (var i = 0; i < submenus.length; i++) {
submenus[i].setAttribute('status', 'closed')
}
for (var i = 0; i < menu_titles.length; i++) {
(function () {
const title = menu_titles[i];
title.addEventListener('click', function () {
var submenu = this.parentElement.querySelector('.submenu')
var status = submenu.getAttribute('status')
if (status === 'closed') {
currentExpand && closeMenu(currentExpand)
currentExpand = submenu
openMenu(submenu)
} else if (status === 'opened') {
closeMenu(submenu)
} else {
return
}
})
})(i)
}
function openMenu(submenu) {
animate({
from: 0,
to: submenu.children[0].clientHeight * submenu.children.length,
totalDuration: 200,
singleDuration: 10,
onAnimate: function (value) {
submenu.style.height = value + 'px'
},
onEnd: function () {
submenu.setAttribute('status', 'opened')
}
})
}
function closeMenu(submenu) {
animate({
from: submenu.clientHeight,
to: 0,
totalDuration: 200,
singleDuration: 10,
onAnimate: function (value) {
submenu.style.height = value + 'px'
},
onEnd: function () {
submenu.setAttribute('status', 'closed')
}
})
}
})()
animate.js
function animate(option) {
var from = option.from
var to = option.to
var totalDuration = option.totalDuration || 500
var singleDuration = option.singleDuration || 10
var onAnimate = option.onAnimate
var onEnd = option.onEnd
var currentValue = from
var totalSteps = Math.floor(totalDuration / singleDuration)
var currentStep = 0
var distance = to - from
var singleStepDistance = distance / totalSteps
var timerId = setInterval(() => {
currentStep++
if (currentStep < totalSteps) {
currentValue += singleStepDistance
} else {
currentValue = to
clearInterval(timerId)
onEnd()
}
onAnimate && onAnimate(currentValue)
}, singleDuration);
}