本实例开发的级联下拉菜单是根据已有json数据创建的DOM元素。点击文本框后,显示一级菜单。如果菜单中包含子菜单,菜单右侧会有指示箭头。点击菜单之后,会再显示下一级菜单,以此类推。当菜单下无子菜单时,选择菜单后会在文本框中显示。

打开后的级联菜单效果如图所示:

使用实例中封装好的插件,只需要有一个input元素,即可通过插件自动生成级联下拉菜单,html代码如下所示:

  <div style="margin-top:100px;text-align:center;">
<input type="text" id="input">
</div>

接下来看下具体封装的js代码怎么实现。

1. 声明级联菜单的构造函数

构造函数需要传入一个文本框元素和菜单关联数据两个参数。

  //elem为文本框,data为菜单关联数据
function CascadeMenu(elem,data){ }

2. 在构造函数中创建级联菜单相关元素,并放到页面中,具体代码如下:

  function CascadeMenu(elem,data){
//获取文本框
this.eInput = elem;
//设置文本框为只读
this.eInput.setAttribute('readonly',true);
//设置文本框提示
this.eInput.placeholder = '请选择';
//获取文本框父元素
var eInputParent = this.eInput.parentNode;
//创建级联菜单容器
this.eCascade = document.createElement('div');
this.eCascade.className = 'cascade_container';
//创建菜单下拉列表容器
this.eCascadeInto = document.createElement('div');
this.eCascadeInto.className = 'cascade_into';
//下拉列表容器默认隐藏
this.eCascadeInto.style.display = 'none';
//将各个元素放到页面中
this.eCascade.appendChild(this.eInput);
this.eCascade.appendChild(this.eCascadeInto);
eInputParent.appendChild(this.eCascade);
//获取菜单数据
this.aData = data;
//记录已选择的菜单数据
this.aSelected = [];
//菜单打开状态,默认为false,表示隐藏
this.bShow = false;
}

3. 在文本框上绑定点击事件,生成级联下拉菜单

刚才已经把需要的元素都放到了页面中,现在可以通过点击文本框显示和隐藏级联菜单元素;
在显示级联菜单元素时,应该要通过数据生成级联下拉菜单。
因为每次点击都需要生成,所以可以在构造函数的原型上添加一个方法。如下所示:

  function CascadeMenu(elem,data){
/*…*/ this.eInput.addEventListener('click',()=>{
//判断菜单打开状态
if(this.bShow){
//如果已打开,则隐藏菜单
this.eCascadeInto.style.display = 'none';
//修改菜单打开状态
this.bShow = false;
}else{
//显示级联菜单元素
this.eCascadeInto.style.display = 'none';
//保存已选择的菜单数据
this.aSelected = this.eInput.value.split('>');
//生成级联菜单
this.generateMenu();
}
});
}
//根据数据生成级联菜单
CascadeMenu.prototype.generateMenu = function(){
//在fnCreatHTML调用实例对象需要声明一个变量指向this
var _self = this;
//因为不确定子菜单有多少组,所以需要声明一个函数来递归调用
//data:传入数据,step:当前级别
function fnCreatHTML(data,step){
//用于存储子菜单数据
var aChildArr = null;
//生成菜单DOM的字符串
var sHTML = '<ul>';
//循环数据
for(let i=0;i<data.length;i++){
//判断如果有子菜单,添加child的class,用于显示菜单右侧箭头
if(data[i].child){
//判断是否是当前选择,如果是,加上cur class,并且存储子菜单数据
if(data[i].name==this.aSelected[step]){
aChildArr = data[i].child;
sHTML += '<li class="child cur" data-po="'+step+'">';
}else{
sHTML += '<li class="child" data-po="'+step+'">';
}
}else{
//如果没有子菜单,直接加到菜单列表中
sHTML += data[i].name == this.aSelected[step]?
'<li class="cur" data-po="'+step+'">':
'<li data-po="'+step+'">';
}
//添加菜单名称
sHTML += data[i].name;
//结束当前菜单
sHTML += '</li>';
}
sHTML += '</ul>';
//如果已选择多个菜单,递归调用函数,生成子菜单
if(this.aSelected.length>step+1){
sHTML += fnCreatHTML(aChildArr,step+1);
}
return sHTML;
}
this.eCascadeInto.innerHTML = fnCreatHTML(this.aData,0);
}

4. 菜单上绑定事件,用于选择菜单
级联菜单有两种类型,一种是有下级菜单的,点击时显示下级菜单;
一种是没有下级菜单的,点击时直接选择菜单并在文本框中按级别显示所选择的菜单。代码如下所示:

  function CascadeMenu(elem,data){
/*…*/ //利用事件委托选择菜单
this.eCascadeInto.addEventListener('click',(event)=>{
//获取菜单
var eTarget = event.target;
//获取选择的级别
var po = +eTarget.dataset.po;
//删除当前选择级后面的数据
this.aSelected.splice(po+1,this.aSelected.length-(po+1));
//修改当前选择数据
this.aSelected[po] = eTarget.innerHTML;
//判断是否有子菜单
if(eTarget.className.indexOf('child')==-1){ //没有子菜单直接选择
this.eInput.value = this.aSelected.join('>');
this.eCascadeInto.style.display = 'none';
this.bShow = false;
}else{ //有子菜单显示下一级
//重新生成DOM元素,数组中增加空字符串用于显示下一级
this.aSelected.push('')
//重新生成级联菜单
this.generateMenu();
}
});
}

5. 在页面空白处点击时,隐藏菜单
现在只能在文本框上点击显示和隐藏菜单。一般来说任何打开的弹框,都希望在弹框以外的位置可以关闭掉。这样需要修改一下文本框上的点击事件函数:当打开菜单时,要在document元素上绑定点击事件,用于关闭菜单;当隐藏菜单时,需要取消document上绑定的点击事件。如下所示:

  function CascadeMenu(elem,data){
/*…*/ this.eInput.addEventListener('click',()=>{
//判断菜单打开状态
if(this.bShow){
//如果已打开,则隐藏菜单
this.eCascadeInto.style.display = 'none';
//修改菜单打开状态
this.bShow = false;
//取消document上的事件
document.onclick = null;
}else{
//显示级联菜单元素
this.eCascadeInto.style.display = 'none';
//保存已选择的菜单数据
this.aSelected = this.eInput.value.split('>');
//生成级联菜单
this.generateMenu();
document.onclick = () => {
//隐藏菜单
this.eCascadeInto.style.display = 'none';
//修改菜单打开状态
this.bShow = false;
//取消document上的事件
document.onclick = null;
}
}
});
//阻止冒泡
this.eCascade.addEventListener('click',(event)=>{
event.stopPropagation();
}); /*…*/
}

6. 最后,准备好数据,调用构造函数,生成级联下拉菜单,如下所示:

  var json = [
{
"name":"北京市","id":"110000","child":[
{"name":"市辖区","id":"110100","child":[
{"name":"东城区","id":"110101","child":null},{"name":"西城区","id":"110102","child":null},{"name":"朝阳区","id":"110105","child":null},{"name":"丰台区","id":"110106","child":null},{"name":"石景山区","id":"110107","child":null},{"name":"海淀区","id":"110108","child":null},{"name":"门头沟区","id":"110109","child":null},{"name":"房山区","id":"110111","child":null},{"name":"通州区","id":"110112","child":null},{"name":"顺义区","id":"110113","child":null},{"name":"昌平区","id":"110114","child":null},{"name":"大兴区","id":"110115","child":null},{"name":"怀柔区","id":"110116","child":null},{"name":"平谷区","id":"110117","child":null},{"name":"密云区","id":"110118","child":null},{"name":"延庆区","id":"110119","child":null}]
},
{"name":"北京市","id":"110000","child":null}
]
},
{
"name":"河北省","id":"130000","child":[
{"name":"石家庄市","id":"130100","child":[
{"name":"市辖区","id":"130101","child":null},{"name":"长安区","id":"130102","child":null},{"name":"桥西区","id":"130104","child":null},{"name":"新华区","id":"130105","child":null},{"name":"井陉矿区","id":"130107","child":null},{"name":"裕华区","id":"130108","child":null},{"name":"藁城区","id":"130109","child":null},{"name":"鹿泉区","id":"130110","child":null},{"name":"栾城区","id":"130111","child":null},{"name":"井陉县","id":"130121","child":null},{"name":"正定县","id":"130123","child":null},{"name":"行唐县","id":"130125","child":null},{"name":"灵寿县","id":"130126","child":null},{"name":"高邑县","id":"130127","child":null},{"name":"深泽县","id":"130128","child":null},{"name":"赞皇县","id":"130129","child":null},{"name":"无极县","id":"130130","child":null},{"name":"平山县","id":"130131","child":null},{"name":"元氏县","id":"130132","child":null},{"name":"赵县","id":"130133","child":null},{"name":"晋州市","id":"130183","child":null},{"name":"新乐市","id":"130184","child":null}]
},
{"name":"唐山市","id":"130200","child":[
{"name":"市辖区","id":"130201","child":null},{"name":"路南区","id":"130202","child":null},{"name":"路北区","id":"130203","child":null},{"name":"古冶区","id":"130204","child":null},{"name":"开平区","id":"130205","child":null},{"name":"丰南区","id":"130207","child":null},{"name":"丰润区","id":"130208","child":null},{"name":"曹妃甸区","id":"130209","child":null},{"name":"滦县","id":"130223","child":null},{"name":"滦南县","id":"130224","child":null},{"name":"乐亭县","id":"130225","child":null},{"name":"迁西县","id":"130227","child":null},{"name":"玉田县","id":"130229","child":null},{"name":"遵化市","id":"130281","child":null},{"name":"迁安市","id":"130283","child":null}]
},
{"name":"秦皇岛市","id":"130300","child":[
{"name":"市辖区","id":"130301","child":null},{"name":"海港区","id":"130302","child":null},{"name":"山海关区","id":"130303","child":null},{"name":"北戴河区","id":"130304","child":null},{"name":"抚宁区","id":"130306","child":null},{"name":"青龙满族自治县","id":"130321","child":null},{"name":"昌黎县","id":"130322","child":null},{"name":"卢龙县","id":"130324","child":null}]
},
{"name":"邯郸市","id":"130400","child":[
{"name":"市辖区","id":"130401","child":null},{"name":"邯山区","id":"130402","child":null},{"name":"丛台区","id":"130403","child":null},{"name":"复兴区","id":"130404","child":null},{"name":"峰峰矿区","id":"130406","child":null},{"name":"邯郸县","id":"130421","child":null},{"name":"临漳县","id":"130423","child":null},{"name":"成安县","id":"130424","child":null},{"name":"大名县","id":"130425","child":null},{"name":"涉县","id":"130426","child":null},{"name":"磁县","id":"130427","child":null},{"name":"肥乡县","id":"130428","child":null},{"name":"永年县","id":"130429","child":null},{"name":"邱县","id":"130430","child":null},{"name":"鸡泽县","id":"130431","child":null},{"name":"广平县","id":"130432","child":null},{"name":"馆陶县","id":"130433","child":null},{"name":"魏县","id":"130434","child":null},{"name":"曲周县","id":"130435","child":null},{"name":"武安市","id":"130481","child":null}]
}
]
},
{
"name":"湖南省","id":"430000","child":[
{"name":"长沙市","id":"430100","child":[
{"name":"市辖区","id":"430101","child":null},{"name":"芙蓉区","id":"430102","child":null},{"name":"天心区","id":"430103","child":null},{"name":"岳麓区","id":"430104","child":null},{"name":"开福区","id":"430105","child":null},{"name":"雨花区","id":"430111","child":null},{"name":"望城区","id":"430112","child":null},{"name":"长沙县","id":"430121","child":null},{"name":"宁乡县","id":"430124","child":null},{"name":"浏阳市","id":"430181","child":null}]
},
{"name":"株洲市","id":"430200","child":[
{"name":"市辖区","id":"430201","child":null},{"name":"荷塘区","id":"430202","child":null},{"name":"芦淞区","id":"430203","child":null},{"name":"石峰区","id":"430204","child":null},{"name":"天元区","id":"430211","child":null},{"name":"株洲县","id":"430221","child":null},{"name":"攸县","id":"430223","child":null},{"name":"茶陵县","id":"430224","child":null},{"name":"炎陵县","id":"430225","child":null},{"name":"醴陵市","id":"430281","child":null}]
},
{"name":"湘潭市","id":"430300","child":[
{"name":"市辖区","id":"430301","child":null},{"name":"雨湖区","id":"430302","child":null},{"name":"岳塘区","id":"430304","child":null},{"name":"湘潭县","id":"430321","child":null},{"name":"湘乡市","id":"430381","child":null},{"name":"韶山市","id":"430382","child":null}]
},
{"name":"衡阳市","id":"430400","child":[
{"name":"市辖区","id":"430401","child":null},{"name":"珠晖区","id":"430405","child":null},{"name":"雁峰区","id":"430406","child":null},{"name":"石鼓区","id":"430407","child":null},{"name":"蒸湘区","id":"430408","child":null},{"name":"南岳区","id":"430412","child":null},{"name":"衡阳县","id":"430421","child":null},{"name":"衡南县","id":"430422","child":[
{"name":"三塘镇",id:"430422",child:null},{"name":"车江镇",id:"430422",child:null}
]},{"name":"衡山县","id":"430423","child":null},{"name":"衡东县","id":"430424","child":null},{"name":"祁东县","id":"430426","child":null},{"name":"耒阳市","id":"430481","child":null},{"name":"常宁市","id":"430482","child":null}]
}
]
},
{
"name":"广东省","id":"440000","child":[
{"name":"广州市","id":"440100","child":[
{"name":"市辖区","id":"440101","child":null},{"name":"荔湾区","id":"440103","child":null},{"name":"越秀区","id":"440104","child":null},{"name":"海珠区","id":"440105","child":null},{"name":"天河区","id":"440106","child":null},{"name":"白云区","id":"440111","child":null},{"name":"黄埔区","id":"440112","child":null},{"name":"番禺区","id":"440113","child":null},{"name":"花都区","id":"440114","child":null},{"name":"南沙区","id":"440115","child":null},{"name":"从化区","id":"440117","child":null},{"name":"增城区","id":"440118","child":null}]
},
{"name":"韶关市","id":"440200","child":[
{"name":"市辖区","id":"440201","child":null},{"name":"武江区","id":"440203","child":null},{"name":"浈江区","id":"440204","child":null},{"name":"曲江区","id":"440205","child":null},{"name":"始兴县","id":"440222","child":null},{"name":"仁化县","id":"440224","child":null},{"name":"翁源县","id":"440229","child":null},{"name":"乳源瑶族自治县","id":"440232","child":null},{"name":"新丰县","id":"440233","child":null},{"name":"乐昌市","id":"440281","child":null},{"name":"南雄市","id":"440282","child":null}]
},
{"name":"深圳市","id":"440300","child":[
{"name":"市辖区","id":"440301","child":null},{"name":"罗湖区","id":"440303","child":null},{"name":"福田区","id":"440304","child":null},{"name":"南山区","id":"440305","child":null},{"name":"宝安区","id":"440306","child":null},{"name":"龙岗区","id":"440307","child":null},{"name":"盐田区","id":"440308","child":null}]
},
{"name":"珠海市","id":"440400","child":[
{"name":"市辖区","id":"440401","child":null},{"name":"香洲区","id":"440402","child":null},{"name":"斗门区","id":"440403","child":null},{"name":"金湾区","id":"440404","child":null}]
}
]
},
{
"name":"南沙群岛","id":"900001","child":null
}
]; var eText = document.getElementById('input');
new CascadeMenu(eText,json);

一个封装好的js级联下拉功能就完成了,可以根据图片自己编写css样式以达到需要的效果。

js面向对象封装级联下拉菜单列表的更多相关文章

  1. JS网页特效操作流程——下拉菜单列表与登录注册弹窗效果

    下拉菜单列表 <style>        *{            margin: 0px;            padding: 0px;        }        .men ...

  2. 第一百三十二节,JavaScript,封装库--下拉菜单

    JavaScript,封装库--下拉菜单 封装库,增加了3个方法 shu_biao_yi_ru_yi_chu()方法,给元素设置鼠标移入移出事件,接收两个参数,参数是移入和移出时的执行函数(包含代码) ...

  3. 在sharepoint 2010创建级联下拉菜单

    SPServices是一个jQuery库,它提取SharePoint Web服务,并使其更容易使用.它可以使用不同的Web服务操作提供更有用且很酷的功能.它完全安装在客户端,不需要服务器. 用SPSe ...

  4. js递归生成树形下拉菜单

    需求:我需要把一个单表的数据转换成类似菜单那种如图所示:我呢需要把这个菜单树放入到下框里面去如图所示: 下面是实现思路:1.第一步1.1var afTypeJson=${afTypeJson}// 这 ...

  5. js+css实现简单下拉菜单

    <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312&qu ...

  6. js实现按钮开关.单机下拉菜单

    通过onclick单击事件,实现效果,代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf- ...

  7. SpinnerViewPop【PopWindow样式(单选)、Dialog样式(单选+多选)的下拉菜单】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 对下拉菜单的文本区域和列表区域进行了封装.包括两种展现方式:popwindow(单选).dialog(单选+多选) 因为该封装需要在 ...

  8. 简单叨叨bootstrap按钮无限层级下拉菜单的实现

    0.写在前面的话 最近看书都懈怠了,又正值新项目,虽说并不是忙得不可开交,好吧我老实交待,我就是偷懒了其实,博客也没更.言归正传,对于前端的不熟悉现在确实是个让我头疼的事情,以至于一些功能要在网络上漫 ...

  9. OAF实现下拉菜单联动

    当需要输入多个下拉菜单选项时,可能某些下拉菜单是有级联关系的.这时候就需要使用级联的下拉菜单来解决.下面的教程将介绍如何使用ppr制作级联下拉菜单 一.新建AM 在test.oracle.apps.c ...

随机推荐

  1. 【Oracle】密码文件相关

    Oracle数据库的orapwd命令,主要用来建立密码(口令)文件. 一.查看帮助信息 [oracle@oracle11g dbs]$ orapwd Usage: orapwd file=<fn ...

  2. 【linux】系统编程-6-POSIX标准下的信号量与互斥锁

    目录 前言 8. POSIX信号量 8.1 概念 8.2 POSIX无名信号量 8.3 POSIX有名信号量 8.4 POPSIX信号量与system V信号量的区别 9. POSIX互斥锁 9.1 ...

  3. 理解C#中的 async await

    前言 一个老掉牙的话题,园子里的相关优秀文章已经有很多了,我写这篇文章完全是想以自己的思维方式来谈一谈自己的理解.(PS:文中涉及到了大量反编译源码,需要静下心来细细品味) 从简单开始 为了更容易理解 ...

  4. 使用.net中的API网关模式封装微服务

    在本文中,我们将了解如何使用API网关模式来封装微服务并抽象出底层实现细节,从而允许使用者拥有进入我们系统的一致入口点. 为了构建和测试我们的应用程序,我们需要: 1.Visual Studio 20 ...

  5. python中IF语句容易犯的错误CASE

    python中没有switch   case类似的语句,但是下面的IF语句却与之类似,却又不同: A = B = C = D = E = 1 if A == 1: B=2 elif B ==2: C= ...

  6. 使用idea插件识别log文件的相关设置

    最近要读一些spring boot项目产生的log文件,众所周知,idea拥有强大的插件系统.当我打开log文件时,idea自动帮我推荐了ideolog这个插件. 但是当我安装好之后发现系统并不能完全 ...

  7. ASP.NET MVC5+EF6+EasyUI 后台管理系统(89)-国际化,本地化,多语言应用

    开篇 早年写过一篇多语言的应用 :   本地化(多语言)   讲述了如何创建多语言的资源文件,并利用资源文件来获得页面和请求的语言属性 本次补充这篇文章,的原因是在实际项目中,有多种需要多语言的情况 ...

  8. 关于springboot项目通过jar包启动之后无法读取项目根路径静态资源

    在一次项目开发过程中,项目根路径下存放了一张图片,生成二维码的时候调用了该图片作为二维码的logo,在windows环境下二维码可以正常生成,但是部署到生产测试环境之后二维码生成报错,FileNotF ...

  9. ovs-ofctl命令

    用于监控和管理 OpenFlow 交换机. 1. 交换机管理命令 查看交换机信息: ovs-ofctl show s1  查看交换机流表: ovs-ofctl dump-tables s1 查看端口信 ...

  10. How to Gracefully Close Channels

    小结: 1. When a goroutine sends a value to a channel, we can view the goroutine releases the ownership ...