通过点击菜单显示或折叠菜单
使用添加删除某个类的样式来实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>二级菜单</title>
<script src="../js/tools.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
*{margin: 0;padding: 0;}
#box{
margin: 50px auto;
width: 200px;
}
.inner{
height: 30px;
overflow: hidden;
}
li{
list-style: none;
height: 30px;
width: 200px;
line-height: 30px;
background: #A6F188;
}
span{
display: block;
width: 200px;
height: 30px;
line-height: 30px;
background: #FFC0CB;
}
/* 展开 */
.unfold{
height: 120px;
overflow: block;
}
</style>
</head>
<body>
<div id="box">
<div class="inner unfold">
<span>音乐</span>
<ul>
<li>曾经的你</li>
<li>七月上</li>
<li>不露声色</li>
</ul>
</div>
<div class="inner">
<span>电影</span>
<ul>
<li>南方车站的聚会</li>
<li>被嫌弃的松子的一生</li>
<li>悲伤逆流成河</li>
</ul>
</div>
<div class="inner">
<span>love</span>
<ul>
<li>胡歌</li>
<li>jia</li>
<li>guo</li>
</ul>
</div>
</div>
<script type="text/javascript">
var oSpan = document.querySelectorAll("span");
var oIndex = oSpan[0].parentNode;
for(var i = 0; i < oSpan.length; i++){
oSpan[i].onclick = function(){
var oDiv = this.parentNode;
//对象切换,并具有过渡效果
guodu(oDiv);
//这个和下面代码效果一样,只是为了方便使用过渡函数
if(oIndex != oDiv && hasClass(oIndex,"unfold")){
//changeStyle(oIndex,"unfold");
guodu(oIndex);
}
/* if(oIndex != oDiv ){
removeClass(oIndex,"unfold");
} */
oIndex = oDiv;
}
}
//实现二级菜单的过渡效果,菜单展开,菜单折叠时具有过渡效果
function guodu(obj){
//先获取切换前的值
var begin = obj.offsetHeight;
changeStyle(obj,"unfold");//切换
var target = obj.offsetHeight; //获取切换后的值
//再将它设置为切换前的值,相当于没有发生变化,单纯的想要获取切换以后的值
obj.style.height = begin+"px";
//将目标值传入封装函数,改变对象的某个属性,实现过渡效果
startMove(obj,{"height":target},function(){
obj.style.height = "";
});
}
</script>
</body>
</html>
tools.js中封装的函数
//传入对象,以及为它添加的类名,可以将这个对象添加上这个类名的样式
function changeStyle(domobj,cName){
if(hasClass(domobj,cName)){
removeClass(domobj,cName);
}else{
addClass(domobj,cName);
}
}
function hasClass(domobj,cName){
var reg = new RegExp("\\b"+cName+"\\b");
if(reg.test(domobj.className)){
return true;
}else{
return false;
}
}
function addClass(domobj,cName){
domobj.className += " "+cName;
}
function removeClass(domobj,cName){
domobj.className = domobj.className.replace(cName,"");
}
//一个对象的css样式发生变化,json传入要变化的属性和目标值
function startMove(domobj,json,fn){
//每一个对象只有一个定时器,防止对同一个对象调用多次定时器,所以在对某个对象调用定时器时
//先清除它之前的定时器
clearInterval(domobj.timer);
domobj.timer = setInterval(()=>{
var flag = true;//为检测属性是否全部执行完,先假设全部执行完,最后再测试,只要有一个
//没有执行完,就设为false,不能清除定时器
for(let attr in json){
var target = json[attr];
var current= parseInt(getStyle(domobj,attr));
//专门检测是否有透明度的变化,因为它的值为小数,所以避免舍入进位时出错,特殊处理
if(attr == "opacity"){
current = getStyle(domobj,attr)*100;
}
var speed = (target-current)/7;
//为达到减速的效果,将每次未走完的长度均分,每次走剩下的几分之一,这样在相同时间内
//走的越来越少,也就达到了减速效果,但这样也产生了一个问题,就是永远走剩下的几分之一,
//永远到不了目标值,所以采用math.ceil和math.floor的方法,分别对应当前值小于目标值,
//当前值大于目标值的时候,这样就可以达到目标值
speed = speed>0?Math.ceil(speed):Math.floor(speed);
if(attr == "opacity"){
domobj.style.opacity = (current+speed)/100;
domobj.style.filter="alpha(opacity:("+(current+speed)+")";
}else{
domobj.style[attr] = current+speed+"px";
}
//如果同时变化的属性有没有执行完的,则不会执行清除定时器的操作
if(current != target){
flag = false;
}
}
if(flag){
clearInterval(domobj.timer);
//第一次要同时变化的属性全部变化完,则执行回调函数,会执行第二次要变化的属性
if(fn){
fn();
}
}
},30)
}
//获取内联或外联的样式值的兼容写法的封装函数
function getStyle(domobj,attr){
if(window.getComputedStyle){
return getComputedStyle(domobj,null)[attr];
}
return domobj.currentStyle[attr];
}