您的位置:首页 > Web前端 > JavaScript

JS实现网页动态折叠菜单

2017-09-12 14:08 337 查看

最近做了一个多级菜单列表动态展示的功能,其中用到了vue.js渐进式框架、aui.css等,在此记录一下,也便于自己日后总结。

说明:vue.js不是必须要用的,因为我们项目中页面用的都是vue,因此我才用的vue方式进行数据绑定,也可以用其他方式实现。

方式一源码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0"/>
<meta name="format-detection" content="telephone=no,email=no,date=no,address=no">
<title>title</title>
<link rel="stylesheet" type="text/css" href="../../css/api.css"/>
<link rel="stylesheet" type="text/css" href="../../css/aui.css"/>
<style>
/*覆盖aui.css初始化的样式*/
ul li ul, ul li ul li {
margin-left: 22px;
}
/*子目录默认隐藏*/
#menu ul li ul {
display: none;
}
/*公开目录列表默认左边距*/
.aui-list1 {
margin-left: 16px;
}
</style>
</head>
<body>
<div id="menu" class="aui-content aui-margin-b-15">
<ul class="aui-list aui-list-in">
<li class="aui-list-header">
<h1>信息公开</h1>
</li>
<li class="aui-list1" v-for="item in list">
<div class="aui-list-item-inner aui-list-item-arrow" @click="toShow()">
{{item.name}}
</div>
<ul  class="aui-list aui-list-in" >
<li class="aui-list" v-for="childnode in item.childnodes">
<div class="aui-list-item-inner aui-list-item-arrow" @click="toShow()">
{{childnode.name}}
</div>
<ul class="aui-list aui-list-in" >
<li class="aui-list-item" v-for="finalnode in childnode.childnodes">
<div class="aui-list-item-label-icon">
<i class="aui-iconfont aui-icon-edit"></i>
</div>
<div class="aui-list-item-inner" @click="toShow()">
{{finalnode.name}}
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</body>
<script type="text/javascript" src="../../script/api.js"></script>
<script src="../../script/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" src="../../script/aui/aui-tab.js"></script>
<script type="text/javascript" src="../../script/common.js"></script>
<script type="text/javascript">
apiready = function() {
//重新赋值
vm_dataList.$data.list = josnobj.data;
};
//用于测试的json字符串
var json = '{"staus":1,"data":[{"id":"1","name":"信息公开指南","url":"???","parentid":"0","childnodes":[]},{"id":"2","name":"信息公开制度","url":"???","parentid":"0","childnodes":[]},{"id":"3","name":"年度公开报告","url":"???","parentid":"0","childnodes":[]},{"id":"4","name":"依申请公开","url":"???","parentid":"0","childnodes":[]},{"id":"5","name":"信息公开目录","url":"???","childnodes":[{"id":"6","name":"机构职能","url":"???","parentid":"5","childnodes":[{"id":"7","name":"省厅职责","url":"???","parentid":"6"},{"id":"8","name":"领导简介","url":"???","parentid":"6"}]},{"id":"9","name":"政府文件","url":"???","parentid":"5","childnodes":[{"id":"10","name":"环保部文件","url":"???","parentid":"9"},{"id":"11","name":"云环发","url":"???","parentid":"9"}]}]}]}';
//将json字符串转为js对象
var josnobj = window.eval("(" + json + ")");
vm_dataList = new Vue({
el : '#menu',
data : {
list : []
},
methods : {
toShow : toShow
}
})
//JS实现控制动态展现闭合的方法
function toShow() {
for (var _obj = document.getElementById("menu").getElementsByTagName("div"), i = -1, em; em = _obj[++i]; ) {
var thisobj = event.target;
//获得当前触发时间的节点对象
var ul = thisobj.nextElementSibling;
//获得当前对象的下一个兄弟节点(此方法忽略"换行"等空白节点)
if (!ul) {
return false;
}
//遍历当前UL对象所有的兄弟节点UL,并判断哪个UL对象是触发事件的当前对象,并完成样式赋予
for (var _li = thisobj.parentNode.parentNode.childNodes, n = -1, li; li = _li[++n]; ) {
if (li.tagName == "LI") {
for (var _ul = li.childNodes, t = -1, $ul; $ul = _ul[++t]; ) {
switch($ul.tagName) {
case "UL":
//用于切换显示、隐藏样式
$ul.style.display = $ul != ul ? "none" : $ul.style.display == "block" ? "none" : "block";
break;
case "":
$ul.style.display = $ul != thisobj ? "none" : thisobj.style.display == "block" ? "none" : "block";
break;
}
}
}
}
}
}
</script>
</html>


效果图:



方式二:

分级加载菜单列表信息。各级的菜单信息并不是一次性全部加载完成,而是当点击一级菜单时,再加载被点击菜单对应的二级菜单信息,以此类推。这样做效率貌似会高点,毕竟不是每个人都需要把所有的菜单都点一遍的。与方式一相比的话,需要注意2点(方式二就不贴源码了,其实和方式一差不多,这里简单谈下思路)

第一:动态效果是JS通过dom控制的,所以在调用dom元素之前必须给对应的菜单列表进行赋值。因为第一种方式是一次性加载所有菜单信息,加载的时候整个dom树的结构已经形成,这种容易控制。但是第二种方式dom树结构是一级接着一级加载的,想要通过dom控制元素,前提是dom树中需要存在这个元素

第二:各级列表需要一个临时容器来存放需要展示的数据,比如我点了一级列表菜单后,后台会调用方法返回一级列表对应的二级列表菜单信息,我把返回的信息保存在我预先设置好的临时容器中,然后再展示到前台页面(也就是通过v-for循环遍历到div中)

我是这样做的:

vm_dataList = new Vue({
el : '#menu',
data : {
list : [],
list2 : [],
list3 : [],
list4 : []
},
methods : {
toShow : toShow
}
})
ps:其实2种方式JS控制展示闭合的那段代码toShow()基本不用动,方式二的话就是再方式一的基础上进行多次循环赋值就可以。其实一开始我用的是方式一,后来进行数据对接的时候发现接口方法是以方式二的方式进行返回数据的,无奈又在方式一的基础上改进成了方式二,在菜单数据量不大的前提下我还是感觉方式一比较好用。                       

dom中获得兄弟节点的2种方法对比:

(1)忽略换行,空格等空节点的方式:

- nextElementSibling 获取当前节点的下一个节点(获得下一个兄弟节点)

- previousElementSibling 获取当前节点的上一个节点

注意: IE将跳过在节点之间产生的空格文档节点(如:换行字符),而Mozilla不会这样——FF会把诸如空格换行之类的排版元素视作节点读取,因此,在ie 中用nextSibling便可读取到的下一个节点元素,在FF中就需要这样写:nextElementSibling了。

(2)获取包含空节点的方式:

-nextSibling

-previousSibling 也是得下一个兄弟节点和上一个兄弟节点的,只是在IE中好用

以上就是我做菜单动态展示的一些经验分享,如果有哪里说得不对的还请大家帮忙更正,多做总结,一起成长!

2017-09-15补充:


    今天根目录用户新增了一条记录后发现导航列表点击无效,然后我就开始找问题,最后发现外层多了一层循环,造成切换样式的方法被多次循环,从而影响了正常的样式赋予,以下是toShow1()方法更改后的源码:

function toShow1() {
//获得当前对象的下一个兄弟节点(此方法忽略"换行"等空白节点),得到需要显示/隐藏的UL对象
var ul = thisobj.nextElementSibling;
if (!ul) {
return false;
}
//遍历触发事件(div)的父节点(UL)下的所有的LI标签
for (var _li = thisobj.parentNode.parentNode.childNodes, n = -1, li; li = _li[++n]; ) {
if (li.tagName == "LI") {
for (var _ul = li.childNodes, t = -1, $ul; $ul = _ul[++t]; ) {
switch($ul.tagName) {
//遍历LI标签下的UL标签(实际需要展现/隐藏的UL对象)
case "UL":
//完成样式赋予,判断如果是触发事件的节点就完成样式切换,反之则设置其display样式为隐藏
$ul.style.display = $ul != ul ? "none" : $ul.style.display == "block" ? "none" : "block";
break;
case "":
$ul.style.display = $ul != thisobj ? "none" : thisobj.style.display == "block" ? "none" : "block";
break;
}
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息