手风琴菜单
在写后台管理页面的时候,编写折叠菜单是一个比较常见的应用场景。那么今天我们就来学习一下怎么使用各种姿势写出手风琴式的折叠菜单。
写之前先看一下效果,插件来源于jQuery之家,演示效果可以点击这里折叠菜单。jquery之家的源码好像有点问题,下载可以点这里。
折叠效果的原理
其实写出这个折叠展开的效果并不复杂,用原生的js代码就可以实现。首先看一下html结构和样式:
折叠菜单
我爱
- 哈士奇
- 大金毛
- 萨摩耶
h4标签就是我们的一级菜单,ul里面的子项就是我们的二级菜单。我们给子菜单设置了高度为0和overflow: hidden
,这样设置后子菜单是默认隐藏的,接下来只要我们只要在js中调整ul的高度就可以了。
原生js实现折叠效果
对于上面的这种html结构,其实代码很简单。
var btn=document.getElementsByClassName("unfold")[0];
var items=document.getElementsByClassName("toggle")[0];
btn.onclick=function () {
items.style.height="70px";
};
由于这里只是简单的讲解原理,所以没有考虑各种复杂的情况。
jq实现折叠效果
jq已经帮我们封装好了slideToggle()
方法,这个方法可以实现点击展开,再次点击收回的效果,所以我们直接使用这个方法就可以了,下面是代码:
var domMenus=$(".menu");
domMenus.on("click",function () {
$(this).next().slideToggle();
})
这里简单的说下jquery的slideToggle方法,在点击之前,它将二级菜单设置为display:none
,在点击后马上将二级菜单设置为display:block;overflow:hidden;height:0
,接着再不停的增加高度,当高度等于二级菜单,也就是ul
的实际高度时,停止增加高度,然后移除这些属性,只保留display:block
这个属性。
组件化的实现
直接像我们上面这么写的确可以实现效果,但是对于代码的复用以及功能的可定制性都比较差,并且暴露了太多的自定义变量,所以我们要对代码进行重构。下面这段代码来自于上面提到的插件源码,源码中变量的命名语义化比较差,所以我对变量重新命名,方便大家观看。
对于html和css中比较重要的部分我简单的介绍一下,下面的这些内容也请结合上面提到的插件源码观看。
html部分请在浏览器中自行观看,不再讲解,css部分有几个比较重要的样式:
.submenu {
display: none;
}
.accordion li.open .link {
color: #b63b4d;
}
.accordion li.open i {
color: #b63b4d;
}
.accordion li.open i.fa-chevron-down {
transform: rotate(180deg);
}
第一个是让二级子菜单默认隐藏,第二个的意思是,在li
有open
这个样式类时,.link
的color
属性变为color: #b63b4d;
,剩下的2个同上。
接着我们再来看js部分:
var Accorrdian=function (menusWrap,multiple) {
this.menusWrap=menusWrap;
this.multiple=multiple|| false;
var links=menusWrap.find('.link');
links.on("click",{menusWrap:menusWrap,multiple:this.multiple},this.dropdown);
};
Accorrdian.prototype.dropdown=function (event) {
var menusWrap=event.data.menusWrap;
var domLink=$(this);
var menuItems=domLink.next();
menuItems.slideToggle();
domLink.parent().toggleClass('open');
if(!event.data.multiple){
menusWrap.find('.submenu').not(menuItems).slideUp().parent().removeClass('open');
}
};
var accordion=new Accorrdian($('#accordion'),false);
首先我们从整体上来看这段代码,这段代码首先定义了一个Accorrdian
的构造函数,然后在这个构造函数的原型上定义了一个dropdown的方法。最后新建了一个Accorrdian的对象,参数有2个。第一个参数是一个jquery的dom对象,它包裹着所有一级菜单,第二个参数是用来设置每次点击的时候能展开的二级菜单的数量,如果为false,则每次只能有一个二级菜单展开,为true则不做限制。
现在看一下这个构造函数:
this.multiple=multiple|| false;
这条语句很有意思,利用了||运算符的特性,当前面一个值为真时,不再对后面的值(代码)作判断(执行)。所以这段代码的意思是当multiple这个值不为空时,this.multiple=multiple
,当multiple
为空时,this.multiple=false
。一句话来说就是给multiple
设置了一个默认值false
。
接着找到menusWrap
下的所有.link
,也就是所有的一级菜单,然后给这些一级菜单注册了一个点击事件,每次点击就执行一次dropdown方法。但是这段注册事件的代码有点特殊:
links.on("click",{menusWrap:menusWrap,multiple:this.multiple},this.dropdown);
它的参数有三个,第二个参数是一个对象,它会传给第三个参数,也就是dropdown
方法中,我们可以在第三个参数,dropdown
方法中取出这个参数,这个数据是挂靠在event
对象的data
属性上,下面的这段代码就是取出这第二个参数:
var menusWrap=event.data.menusWrap;
然后我们再看dropdown这个方法,首先它找到当前被点击的这个一级菜单的下一个dom节点,也就是这个一级菜单对应的二级菜单,然后对这个二级菜单执行
slideToggle()
方法,接着再给这个一级菜单添加opne
样式类,改变一级菜单的样式。然后我们再看一下最后这个if
语句:
if(!event.data.multiple){
menusWrap.find('.submenu').not(menuItems).slideUp().parent().removeClass('open');
}
这里首先判断我们新建对象的时候第二个参数是true
还是false
,如果是true
不再执行,如果是false
则开始执行代码。首先它找到所有的二级菜单,当前被点击的一级菜单对应的二级菜单除外,然后对这些所有的二级菜单执行slideUp
方法,也就是将所有的二级菜单收起。
至此,一个可定制可复用的折叠菜单插件已经完成了。