js面向对象封装级联下拉菜单列表的实现步骤


Posted in Javascript onFebruary 08, 2021

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

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

js面向对象封装级联下拉菜单列表的实现步骤

使用实例中封装好的插件,只需要有一个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面向对象封装级联下拉菜单列表的实现步骤的详细内容,更多关于js 封装下拉菜单的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
javascript 清除输入框中的数据
Apr 13 Javascript
javascript奇异的arguments分析
Oct 20 Javascript
Chrome下ifame父窗口调用子窗口的问题示例探讨
Mar 17 Javascript
javascript匿名函数实例分析
Nov 18 Javascript
Flow之一个新的Javascript静态类型检查器
Dec 21 Javascript
JQuery ztree 异步加载实例讲解
Feb 25 Javascript
Bootstrap popover 实现鼠标移入移除显示隐藏功能方法
Jan 24 Javascript
JS获取当前时间的实例代码(昨天、今天、明天)
Nov 13 Javascript
详解Express笔记之动态渲染HTML(新手入坑)
Dec 13 Javascript
react用Redux中央仓库实现一个todolist
Sep 29 Javascript
js实现无缝轮播图效果
Mar 09 Javascript
js调用网络摄像头的方法
Dec 05 Javascript
JavaScript实现点击出现子菜单效果
Feb 08 #Javascript
深入理解javascript中的this
Feb 08 #Javascript
Vue中使用wangeditor富文本编辑的问题
Feb 07 #Vue.js
vue使用lodop打印控件实现浏览器兼容打印的方法
Feb 07 #Vue.js
js基于canvas实现时钟组件
Feb 07 #Javascript
nestjs中异常过滤器Exceptionfilter的具体使用
Feb 07 #Javascript
js实现类选择器和name属性选择器的示例步骤
Feb 07 #Javascript
You might like
WINDOWS服务器安装多套PHP的另类解决方案
2006/10/09 PHP
一个php作的文本留言本的例子(三)
2006/10/09 PHP
php结合飞信 免费天气预报短信
2009/05/07 PHP
PHP函数篇详解十进制、二进制、八进制和十六进制转换函数说明
2011/12/05 PHP
THINKPHP内容分页代码分享
2015/01/14 PHP
PHP贪婪算法解决0-1背包问题实例分析
2015/03/23 PHP
php实现上传图片文件代码
2015/07/19 PHP
图文详解PHP环境搭建教程
2016/07/16 PHP
JS中的public和private对象,即static修饰符
2012/01/18 Javascript
javascript学习笔记(十七) 检测浏览器插件代码
2012/06/20 Javascript
js关闭父窗口时关闭子窗口
2013/04/01 Javascript
nodejs 的 session 简单使用
2016/06/06 NodeJs
vue mounted组件的使用
2018/06/18 Javascript
vue将单页面改造成多页面应用的方法
2018/11/25 Javascript
JavaScript 常见的继承方式汇总
2020/09/17 Javascript
解决vue打包 npm run build-test突然不动了的问题
2020/11/13 Javascript
k8s node节点重新加入master集群的实现
2021/02/22 Javascript
Python实现动态添加类的属性或成员函数的解决方法
2014/07/16 Python
Python通过matplotlib绘制动画简单实例
2017/12/13 Python
python+opencv实现摄像头调用的方法
2019/06/22 Python
使用Python opencv实现视频与图片的相互转换
2019/07/08 Python
python装饰器代替set get方法实例
2019/12/19 Python
解决python ThreadPoolExecutor 线程池中的异常捕获问题
2020/04/08 Python
Python爬虫获取页面所有URL链接过程详解
2020/06/04 Python
Python3爬虫里关于代理的设置总结
2020/07/30 Python
python实现画图工具
2020/08/27 Python
美国婴儿和儿童家具网上商店:ABaby.com
2018/07/02 全球购物
八年级历史教学反思
2014/01/10 职场文书
合作意向书格式及范文
2014/03/31 职场文书
倡议书范文
2014/04/16 职场文书
志愿者宣传口号
2014/06/17 职场文书
班级光棍节联谊会策划书
2014/10/10 职场文书
领导干部个人整改措施落实情况汇报
2014/10/29 职场文书
辩论会主持词
2015/07/03 职场文书
sass 常用备忘案例详解
2021/09/15 HTML / CSS
MYSQL事务的隔离级别与MVCC
2022/05/25 MySQL