本实例开发的级联下拉菜单是根据已有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. 数据库MySQL(带你零基础入门MySQL)

    (一)认识数据库 redis默认端口:6379 mysql默认端口:3306 什么是数据库? 数据库的英文单词:data base,简称DB. 数据库实际上就是一个文件集合,是一个存储数据的仓库,本质 ...

  2. MySQL常用的数据类型和字段属性

    数据类型 数值 tinyint 十分小的数据 1个字节 smallint 较小的数据 2个字节 mediumint 中等大小的数据 3个字节 int 标准的整数 4个字节 常用 bigint 较大的数 ...

  3. pandas 写csv 操作

    pandas 写csv 操作 def show_history(self): df = pd.DataFrame() df['Time'] = pd.Series(self.time_hist) df ...

  4. SDUST数据结构 - chap7 图

    判断题: 选择题: 函数题: 6-1 邻接矩阵存储图的深度优先遍历: 裁判测试程序样例: #include <stdio.h> typedef enum {false, true} boo ...

  5. C++导言与入门

    写在开始 计算机编程语言: Remember that a program is just a sequence of instructions telling a computer what to ...

  6. 好你个C语言,原来还有这么多副面孔!

    C语言可以这样比喻,是一门非常强大的内功心法,学会它可以做到一法通万法.这也是它至今不衰的原因.说了这么多C语言的优点,现在来说说它的缺点.C语言最大的优点也是它最大的缺点,拥有强大的力量时应时刻保持 ...

  7. 前端中的script标签

    script标签中的重要属性! . 浏览器解析行内脚本的方式决定了它在看到字符串时,会将其当成结束的 标签.想避免这个问题,只需要转义字符"\" ①即可: 要包含外部文件中的 Ja ...

  8. Django QuerySet API---数据库接口

    基本的创建与查询 # -*- coding: utf-8 -*- from django.http import HttpResponse from TestModel.models import T ...

  9. vue-cli快速创建项目,可视化创建

    之前学习了交互式创建,发现过程无聊,而且不方便,后面又学习了图形可视化创建,下面进行分享 1.打开cmd 2.输入vue ui,输入后会出现如下 C:\Users\12235>vue ui St ...

  10. PE节表