折叠菜单
功能需求:
- 根据数据内容创建折叠菜单内容,显示在页面中;
- 点击一级菜单,如果点击的一级菜单下有对应的子菜单,则让其展开;如果没有子菜单,则不能点击;
- 要考虑到三级菜单、四级菜单…的显示;
先来看一下效果:
因为不确定一共有几级菜单,考虑到多级菜单出现的情况,可以使用递归回调函数;数据结构一定要统一,方便对数据进行操作。
下面附上代码
html代码,模拟菜单数据,实例化折叠菜单:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>foldMenu</title>
</head>
<body>
<script type="module">
import Menu from './js/Menu.js';
var arr=[
{name:"今日爆款",category:[
{name:"热销商品",href:"https://blog.csdn.net/Charissa2017/article/details/103855079"},{name:"店长推荐",href:"https://blog.csdn.net/Charissa2017/article/details/103855079"}
]},
{name:"新品上市",category:[
{name:"施华洛世奇联名故宫宫廷文化",category:[
{name:"故宫文创",category:[
{name:"御猫",href:"https://blog.csdn.net/Charissa2017/article/details/103855079"},{name:"紫禁城里过大年",href:"https://blog.csdn.net/Charissa2017/article/details/103855079"}
]},
{name:"故宫博物馆",href:"https://blog.csdn.net/Charissa2017/article/details/103855079"}
]},{name:"12月新品",href:"https://blog.csdn.net/Charissa2017/article/details/103855079"}
]},
{name:"折扣专区",category:[]},
{name:"专柜爆款",category:[
{name:"时尚尖货",href:"https://blog.csdn.net/Charissa2017/article/details/103855079"},{name:"专柜爆款",href:"https://blog.csdn.net/Charissa2017/article/details/103855079"}
]},
{name:"品牌畅销系列",category:[
{name:"BEE A QUEEN女王系列",href:""},
{name:"蓝色天鹅专区",category:[
{name:"白天鹅",href:""},{name:"黑天鹅",href:""},{name:"蓝色天鹅",href:""}
]},{name:"MY HERO",href:""},{name:"LISABEL蜜蜂",href:""},
{name:"Sparkling Dance",href:""},{name:"Remix系列",href:""},{name:"Magic唯美雪花",href:""},{name:"MICKEY&MINNIE系列",href:""}
]},
]
init();
function init(){
let menu=new Menu(arr);
menu.appendTo("body");
}
</script>
</body>
</html>
Menu.js文件,用来实现对数据的操作处理,创建菜单:
import Utils from './Utils.js';
export default class Menu{
constructor(_list){
this.elem=this.createElem(_list);
}
createElem(_list){
if(this.elem) return this.elem;
//创建最外层ul容器
let ul=Utils.createE("ul",{
listStyle:"none",
padding:"0px",
margin:"0px",
width:"235px",
border:"1px solid #f5f5f5",
color:"#333",
fontSize:"14px",
userSelect: "none"
});
//创建li列表
this.createMenu(_list,ul);
//ul监听点击事件
ul.addEventListener("click",e=>this.clickHandler(e));
return ul;
}
appendTo(parent){
Utils.appendTo(this.elem,parent);
}
//创建一级菜单
createMenu(_list,parent){
for(let i=0;i<_list.length;i++){
let li=Utils.createE("li",{
background:"#f5f5f5",
borderBottom:"1px solid #ddd",
lineHeight:"30px",
cursor:"pointer"
},{
textContent:_list[i].name
})
let span=Utils.createE("span",{
display:"inline-block",
width:"16px",
height:"16px",
background:"#ddd",
lineHeight:"12px",
textAlign:"center",
float:"left",
margin:"7px 15px 0px 8px"
},{
textContent:_list[i].category.length>0? "+" : "-"
})
Utils.appendTo(span,li);
Utils.appendTo(li,parent);
//创建子菜单
this.createSubMenu(_list[i].category,li);
}
}
//创建子菜单
createSubMenu(_subList,parent){
//如果当前菜单没有子菜单,则跳出
if(_subList.length===0) return;
let subUl=Utils.createE("ul",{
listStyle:"none",
background:"#fff",
padding:"0px",
margin:"0px",
fontSize:"14px",
display:"none"
})
for(let i=0;i<_subList.length;i++){
let subLi=Utils.createE("li",{
paddingLeft:"40px",
position:"relative"
})
if(!_subList[i].category){
//如果当前菜单没有子菜单,则创建a标签,进行跳转
let subA=Utils.createE("a",{
color:"#333",
textDecoration:"none",
width:"100%",
display:"inline-block"
},{
textContent:_subList[i].name,
href:_subList[i].href || "javascript:void(0)",
target:_subList[i].href ? "_blank" : "_self"
})
Utils.appendTo(subA,subLi);
}else{
//如果当前菜单有子菜单,创建span标签
let subSpan=Utils.createE("span",{
position:"absolute",
left:"20px",
top:"8px",
border: "1px solid #ccc",
display: "inline-block",
width: "10px",
height: "10px",
lineHeight:"8px"
},{
textContent:_subList[i].category.length>0? "+" : "-"
})
subLi.textContent=_subList[i].name;
Utils.appendTo(subSpan,subLi);
}
Utils.appendTo(subLi,subUl);
//如果当前菜单没有子菜单,则跳过下面的执行
if(!_subList[i].category) continue;
//如果当前菜单有子菜单,将子菜单作为参数,进行递归
this.createSubMenu(_subList[i].category,subLi);
}
Utils.appendTo(subUl,parent);
}
clickHandler(e){
//如果当前点击的不是li标签或者span,直接跳出
if(e.target.nodeName!=="LI" && e.target.nodeName!=="SPAN") return;
let targ;
//让targ等于当前点击的li标签
if(e.target.nodeName==="SPAN") targ=e.target.parentElement;
else targ=e.target;
//如果当前点击Li下面没有子菜单,直接跳出
if(targ.children.length<=1) return;
//控制当前点击的Li下的ul显示隐藏
if(!targ.bool) targ.lastElementChild.style.display="block";
else targ.lastElementChild.style.display="none";
targ.bool=!targ.bool;
//改变span标签的内容
this.changeSpan(targ);
}
changeSpan(elem){
//改变span的显示内容
if(elem.lastElementChild.style.display==="block"){
elem.firstElementChild.textContent="-";
}else{
elem.firstElementChild.textContent="+";
}
}
}
Utils.js文件,是一个工具包:
export default class Utils{
static createE(elem,style,prep){
elem=document.createElement(elem);
if(style) for(let prop in style) elem.style[prop]=style[prop];
if(prep) for(let prop in prep) elem[prop]=prep[prop];
return elem;
}
static appendTo(elem,parent){
if (parent.constructor === String) parent = document.querySelector(parent);
parent.appendChild(elem);
}
static randomNum(min,max){
return Math.floor(Math.random*(max-min)+min);
}
static randomColor(alpha){
alpha=alpha||Math.random().toFixed(1);
if(isNaN(alpha)) alpha=1;
if(alpha>1) alpha=1;
if(alpha<0) alpha=0;
let col="rgba(";
for(let i=0;i<3;i++){
col+=Utils.randomNum(0,256)+",";
}
col+=alpha+")";
return col;
}
static insertCss(select,styles){
if(document.styleSheets.length===0){
let styleS=Utils.createE("style");
Utils.appendTo(styleS,document.head);
}
let styleSheet=document.styleSheets[document.styleSheets.length-1];
let str=select+"{";
for(var prop in styles){
str+=prop.replace(/[A-Z]/g,function(item){
return "-"+item.toLocaleLowerCase();
})+":"+styles[prop]+";";
}
str+="}"
styleSheet.insertRule(str,styleSheet.cssRules.length);
}
static getIdElem(elem,obj){
if(elem.id) obj[elem.id]=elem;
if(elem.children.length===0) return obj;
for(let i=0;i<elem.children.length;i++){
Utils.getIdElem(elem.children[i],obj);
}
}
}