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 相关文章推荐
window.parent与window.openner区别介绍
Apr 12 Javascript
jquery监听div内容的变化具体实现思路
Nov 04 Javascript
推荐一个封装好的getElementsByClassName方法
Dec 02 Javascript
js简单的点击返回顶部效果实现方法
Apr 10 Javascript
drag-and-drop实现图片浏览器预览
Aug 06 Javascript
JS控制FileUpload的上传文件类型实例代码
Oct 07 Javascript
vuex实现简易计数器
Oct 27 Javascript
微信小程序中form 表单提交和取值实例详解
Apr 20 Javascript
解决vue项目打包后提示图片文件路径错误的问题
Jul 04 Javascript
axios如何利用promise无痛刷新token的实现方法
Aug 27 Javascript
详解如何在JS代码中消灭for循环
Dec 11 Javascript
jQuery实现动态向上滚动
Dec 21 jQuery
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
PHP 和 XML: 使用expat函数(三)
2006/10/09 PHP
smarty section简介与用法分析
2008/10/03 PHP
php 常用类整理
2009/12/23 PHP
PHP动态生成javascript文件的2个例子
2014/04/11 PHP
php插入排序法实现数组排序实例
2015/02/16 PHP
JS面向对象编程 for Cookie
2010/09/19 Javascript
jqgrid 表格数据导出实例
2013/11/21 Javascript
JS实现仿新浪微博发布内容为空时提示功能代码
2015/08/19 Javascript
Webwork 实现文件上传下载代码详解
2016/02/02 Javascript
jQuery Tags Input Plugin(添加/删除标签插件)详解
2016/06/20 Javascript
基于JavaScript实现百度搜索框效果
2020/06/28 Javascript
Bootstrap栅格系统的使用详解
2017/10/30 Javascript
js实现金山打字通小游戏
2020/07/24 Javascript
[01:04:39]OG vs Mineski 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
[59:00]OG vs TNC 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
Python基于回溯法子集树模板实现8皇后问题
2017/09/01 Python
基于python实现的百度音乐下载器python pyqt改进版(附代码)
2019/08/05 Python
详解Django-channels 实现WebSocket实例
2019/08/22 Python
Python Pandas 对列/行进行选择,增加,删除操作
2020/05/17 Python
python实现按键精灵找色点击功能教程,使用pywin32和Pillow库
2020/06/04 Python
Python应用实现处理excel数据过程解析
2020/06/19 Python
Python第三方包PrettyTable安装及用法解析
2020/07/08 Python
python hmac模块验证客户端的合法性
2020/11/07 Python
css3绘制百度的小度熊
2018/10/29 HTML / CSS
HTML5 标准将把互联网视频扔回到黑暗时代
2010/02/10 HTML / CSS
高中生职业规划范文
2014/03/09 职场文书
法人代表任命书范本
2014/06/05 职场文书
班级学雷锋活动总结
2014/06/26 职场文书
团队拓展活动方案
2014/08/28 职场文书
领导干部贪图享乐整改措施
2014/09/21 职场文书
机电专业毕业生自我鉴定2014
2014/10/04 职场文书
大学生党性分析材料
2014/12/19 职场文书
感谢信怎么写
2015/01/21 职场文书
浅谈tf.train.Saver()与tf.train.import_meta_graph的要点
2021/05/26 Python
Tomcat用户管理的优化配置详解
2022/03/31 Servers
virtualenv隔离Python环境的问题解析
2022/06/21 Python