本实例开发的级联下拉菜单是根据已有json数据创建的DOM元素。点击文本框后,显示一级菜单。如果菜单中包含子菜单,菜单右侧会有指示箭头。点击菜单之后,会再显示下一级菜单,以此类推。当菜单下无子菜单时,选择菜单后会在文本框中显示。
打开后的级联菜单效果如图所示:
使用实例中封装好的插件,只需要有一个input元素,即可通过插件自动生成级联下拉菜单,html代码如下所示:
1
2
3
|
< div style = "margin-top:100px;text-align:center;" > < input type = "text" id = "input" > </ div > |
接下来看下具体封装的js代码怎么实现。
1. 声明级联菜单的构造函数
构造函数需要传入一个文本框元素和菜单关联数据两个参数。
1
2
3
4
|
//elem为文本框,data为菜单关联数据 function CascadeMenu(elem,data){ } |
2. 在构造函数中创建级联菜单相关元素,并放到页面中,具体代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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. 在文本框上绑定点击事件,生成级联下拉菜单
刚才已经把需要的元素都放到了页面中,现在可以通过点击文本框显示和隐藏级联菜单元素;
在显示级联菜单元素时,应该要通过数据生成级联下拉菜单。
因为每次点击都需要生成,所以可以在构造函数的原型上添加一个方法。如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
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. 菜单上绑定事件,用于选择菜单
级联菜单有两种类型,一种是有下级菜单的,点击时显示下级菜单;
一种是没有下级菜单的,点击时直接选择菜单并在文本框中按级别显示所选择的菜单。代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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上绑定的点击事件。如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
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. 最后,准备好数据,调用构造函数,生成级联下拉菜单,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
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面向对象封装级联下拉菜单列表的实现步骤的详细内容,更多关于js 封装下拉菜单的资料请关注服务器之家其它相关文章!
原文链接:https://www.cnblogs.com/jiangweiping/p/14386548.html