虽然网上有许多的实现下拉树列表菜单的实现,但都是基于某一框架的,感觉使用起来过于复杂。
为了巩固学习的JS知识,于是尝试着自己写一个。
这个下拉树列表整体放置在一个div中。为了使其看起来和HTML自带的下拉列表类似,我选择了使用select标签,但一开始并不在其中嵌套option选项。(其实也可以用input自己实现一个,只不过我觉得这样用方便也美观......)
在select标签之后跟着一个div,主要用来放置目录列表,目录用ul标签嵌套的方式实现。该div使用绝对定位(这样在div显示的时候就不会占用文档空间),并且初始出于隐藏状态。
通过给select标签添加click事件监听,使目录div显示。
在选择目录后,生成一个option选项,将其加入到select中并将其选中,但option还是要隐藏,就可以在select中设置值了。(因为好像select不能直接通过给value赋值的方法赋值,所以只能采用这个折中的办法了。如果是用的input实现的,那么直接对其value赋值,就没这么麻烦。)
目前我只是完成一个粗略的结构,树形结构的展开和收缩功能也还没有加上,先写下这些东西记录下学习成果。随后逐步完善。
下面是代码
<!DOCTYPE html>
<html>
<head>
<title>Test2</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div>TODO write content</div>
<!-- 下来树列表 -->
<div style="width:200px;">
<select style="width:200px;padding: 0;margin: 0;" οnclick="createTree(this)"></select>
<div style="width:200px;padding-top:2px;margin: 0;background-color: #ccccff;display: none;overflow: auto;position: absolute;">
<ul>
<li><a href="javascript:void(0);" title="A">A</a>
<ul>
<li><a href="javascript:void(0);" title="A1">A1</a></li>
<li><a href="javascript:void(0);" title="A2">A2</a>
<ul>
<li><a href="javascript:void(0);" title="AA2">AA2</a></li>
<li><a href="javascript:void(0);" title="AA3">AA3</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="javascript:void(0);" title="B">B</a>
<ul>
<li><a href="javascript:void(0);" title="B1">B1</a></li>
<li><a href="javascript:void(0);" title="B2">B2</a></li>
</ul>
</li>
<li><a href="javascript:void(0);" title="C">C</a></li>
</ul>
</div>
</div>
<script>
function createTree(component){
//为select定义一个blur属性,用于标记select是否失去焦点
//这个属性将在select失去焦点时隐藏目录div是用到
//因为不能简单的设置当select失去焦点时就隐藏目录div
//这样的话点击目录时就会发生冲突,导致目录提前隐藏而没有选中值
component.blur = false;
component.addEventListener("blur",function(){
this.blur = true;
});
//获得目录列表,其中每两个标签之间有一个文本节点,所以select的父节点有5个子节点
//而包含列表的div是第四个节点
var parent = component.parentNode;
var treeMenu = parent.getElementsByTagName("div")[0];
console.log("select is clicked "+component.blur);
//点击select时,列表是展开的就收缩列表,否则就展开列表
if(treeMenu.style.display === "none"){
treeMenu.style.display = "";
console.log("display");
}
else{
treeMenu.style.display = "none";
console.log("select hidden");
}
//获得每一个目录的link,并且给每一个link都绑定一个事件
//当该目录被选中时,就给select插入一个option,并且选中该option
//并且将该option设置为隐藏的,这样就设置了select的值
var menuLinks = treeMenu.getElementsByTagName("a");
//console.log(menuLinks[0].innerHTML);
for(var i=0;i<menuLinks.length;i++){
//console.log(menuLinks[i].innerHTML);
menuLinks[i].addEventListener("click",function(){
console.log("Link is clicked");
var text = this.innerHTML;
var option = document.createElement("option");
option.text = text;
option.selected = true;
option.style.display = "none";
component.add(option,null);
treeMenu.style.display = "none";
console.log("link hidden");
});
}
//给body绑定一个点击事件,这个事件用于检测鼠标是否在下拉树列表外进行了点击
//如果是,则要隐藏下拉树,相当于下拉树列表失去了焦点
//判断的方法是根据发生点击事件是鼠标的位置
document.body.addEventListener("click",function(event){
console.log("body is clicked");
if(treeMenu.style.display !== "none"){
/*console.log("top: "+treeMenu.offsetTop+" left: "+treeMenu.offsetLeft+
" height: "+treeMenu.offsetHeight+" width: "+treeMenu.offsetWidth);
console.log("eventX: "+event.clientX+" eventY: "+event.clientY);
console.log(event.type);*/
if((event.clientX<treeMenu.offsetLeft ||
event.clientX>(treeMenu.offsetLeft+treeMenu.offsetWidth) ||
event.clientY<treeMenu.offsetTop ||
event.clientY>(treeMenu.offsetTop+treeMenu.offsetHeight))
&& component.blur){
treeMenu.style.display = "none";
console.log("body hidden");
}
}
});
//给window绑定事件隐藏目录
//因为body并不一定能覆盖完整个浏览器窗口
//检测点击是否发生在body之外,如果是,就应该隐藏目录
//依然要检测select的blur属性,不然会有一个小bug
//在select边缘点击的时候,如果不检测select blur属性,会触发目录隐藏
window.addEventListener("click",function(event){
console.log("window is clicked");
//console.log(event.clientY);
if((event.clientX<document.body.offsetLeft ||
event.clientX>(document.body.offsetLeft+document.body.offsetWidth) ||
event.clientY<document.body.offsetTop ||
event.clientY>(document.body.offsetTop+document.body.offsetHeight))
&& component.blur)
if(treeMenu.style.display !== "none"){
treeMenu.style.display = "none";
console.log("widow hidden");
}
});
}
</script>
</body>
</html>