原生js实现下拉框选择组件


Posted in Javascript onJanuary 20, 2021

本文实例为大家分享了js实现下拉框选择组件的具体代码,供大家参考,具体内容如下

功能需求:

1、点击div后,div显示聚焦状态,同时显示下拉框内容;
2、选择儿童人数后,如果儿童人数大于0,在下方出现对应的儿童年龄选择框数量;
3、成人人数的选择范围是1-7,儿童人数的选择范围是0-4,儿童年龄的选择范围是<1、1-17;
4、点击确认按钮后,将选择好的成人人数和儿童人数显示在最上方的div内;
5、可以控制选择框是否可点击;
6、当显示一个ul列表时,点击另一个ul列表,将上一个ul列表隐藏;
7、点击隐藏框内除绑定事件元素外,将正在显示的ul列表隐藏;
8、点击页面中任意空白位置,将显示的下拉框内容整体隐藏;

下拉框不可操作时的显示状态:

原生js实现下拉框选择组件

下拉框可操作时:

原生js实现下拉框选择组件

选择儿童人数后,下方自动出现对应数量的儿童年龄选择框:

原生js实现下拉框选择组件

点击确认按钮后,将结果显示在是上方的div内:

原生js实现下拉框选择组件

刚开始的想法是对select、ul下拉列表、btn按钮分别进行事件监听,此外还要有当点击下拉框内其它位置时,ul下拉列表隐藏、当点击body时整个下拉框内容隐藏。监听事件过多,而且事件冒泡也会影响事件的执行,导致某些事件会出现执行多次的情况。

儿童年龄的选择框是根据儿童的人数来生成的,有几个儿童,就有几个年龄选择框。这种情况下,年龄的选择框肯定是动态创建的,无法针对年龄的select进行事件监听,只能采用事件委托的形式,所以最后把对select、ul下拉列表、btn按钮的点击事件,还有当点击container内其它位置时,ul下拉列表隐藏。全部委托给了dropDownContainer元素。

下面附上代码

html结构代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>select</title>
</head>
<body>
  <script type="module">
    import Main from './js/Main.js';
    //参数为false时,选择框不可点击;为true时,选择框可使用
    let main=new Main(true);
    main.appendTo("body");
  </script>
</body>
</html>

Main.js文件:

import Utils from './Utils.js';
export default class Main{
  static styles=false;
  listPrep;
  constructor(state){
    //state控制下拉框是否可点击
    this.state=state;
    this.elem=this.createE();
  }
  createE(){
    if(this.elem) return this.elem;
    let div=Utils.createE("div");
    div.className="guestsNum";
    div.innerHTML=`<span>人数未定</span><i></i>
    <div class="dropDownContainer none" id="dropDownContainer">
      <div class="dropDownItem clearfix">
        <span>每间</span>
        <div class="dropDownSelect">
          <div class="dropDownCont"><span id="adultNum">2 成人</span><i></i></div>
          <ul class="dropDownList" tag="adult">${this.setDropDownList("adult")}</ul>
        </div>
        <div class="dropDownSelect">
          <div class="dropDownCont"><span id="childrenNum">0 儿童</span><i></i></div>
          <ul class="dropDownList" tag="children"><li>0</li>${this.setDropDownList("children")}</ul>
        </div>
      </div>
      <div class="dropDownItem clearfix none" id="ItemAge"></div>
      <div class="dropDownBottom clearfix">
        ${this.state?'':'<em class="dropDownTips">请优先选择日期,以便查询实时价格。</em>'}
        ${this.state?'<a class="dropDownBtn" id="dropDownBtn" href="javascript:void(0)" rel="external nofollow" rel="external nofollow" >确认</a>':'<a class="dropDownBtn disabled" href="javascript:void(0)" rel="external nofollow" rel="external nofollow" >确认</a>'}
      </div>
    </div>`;
    //设置样式,因为样式只设置一次就好,不需要实例化,所以使用静态方法
    Main.setStyles();
    //获取元素
    Utils.getIdElem(div,this);
    //监听div的点击事件
    div.addEventListener("click",(e)=>this.guestsNumClickHandler(e));
    //如果state为true,下拉框监听点击事件
    if(this.state) this.dropDownContainer.addEventListener("click",e=>this.dropDownContainerClick(e));
    //document监听点击事件,隐藏下拉框
    document.addEventListener("click",e=>this.documentClick(e));
    return div;
  }
  appendTo(parent){
    Utils.appendTo(this.elem,parent);
  }
  guestsNumClickHandler(e){
    //如果下拉框是显示状态,则直接跳出,避免重复操作
    if(!Utils.hasClass(this.dropDownContainer,"none")) return;
    //如果点击的不是guestsNum,直接跳出,避免事件冒泡
    if(e.target.nodeName!=="SPAN"&&e.target.nodeName!=="I"&&!Utils.hasClass(e.target,"guestsNum")) return;
    //给div添加聚集样式
    Utils.addClass(this.elem,"focus");
    //将dropDownContainer显示
    Utils.removeClass(this.dropDownContainer,"none");
  }
  dropDownContainerClick(e){
    if(e.target.nodeName==="LI"){
      //点击ul选择列表
      this.dropDownListClick(e);
    }
    else if(e.target.id==="dropDownBtn"){
      //点击确认按钮
      this.dropDownBtnClick();
    }
    else if(e.target.nodeName==="SPAN" || e.target.nodeName==="I") {
      //点击span或者i标签时,将它们的父元素div作为参数
      this.dropDownSelectClick(e.target.parentElement);
    }
    else if(Utils.hasClass(e.target,"dropDownCont")){
      //点击div选择框时,将div作为参数
      this.dropDownSelectClick(e.target);
    }
    else {
      //点击下拉框内其它位置时,让当前的ul列表隐藏
      if(this.listPrep) this.listPrep.style.display="none";
    }
  }
  dropDownSelectClick(div){
    //隐藏掉上一个显示的ul列表
    if(this.listPrep) this.listPrep.style.display="none";
    //当前点击的ul列表赋值给this.listPrep
    this.listPrep=div.nextElementSibling;
    //将当前点击的ul列表显示
    this.listPrep.style.display="block";
  }
  dropDownListClick(e){
    //获取当前点击的ul的tag属性值
    let tag=this.listPrep.getAttribute("tag");
    let unit="";
    switch (tag){
      case "adult": unit="成人";break;
      case "children": 
        unit="儿童";
        let txt=Number(e.target.innerText);
        //根据li的数值,自动创建下面的年龄选择框
        this.setDropDownItemAge(txt);
        break;
      case "age": unit="岁";break;
    }
    //将选择的li的值,显示出来
    this.listPrep.previousElementSibling.firstElementChild.textContent=e.target.innerText+" "+unit;
    //显示完成后,将当前显示的ul隐藏
    this.listPrep.style.display="none";
  }
  setDropDownItemAge(txt){
    let str="<span>儿童年龄</span>";
    if(txt===0){
      //如果是0,则年龄选择框不显示
      this.ItemAge.style.display="none";
    }else{
      this.ItemAge.style.display="block";
      //循环选择的数值,创建年龄选择框
      for(let i=0;i<txt;i++){
        str+=`<div class="dropDownSelect">
        <div class="dropDownCont"><span><1岁</span><i></i></div>
        <ul class="dropDownList" tag="age"><li><1</li>${this.setDropDownList("age")}</ul>
      </div>`;
      }
      this.ItemAge.innerHTML=str;
    }
  }
  dropDownBtnClick(){
    //将选择的内容显示在最上方的select框内
    let resultStr=this.adultNum.innerText.replace(/\s/g,"")+" "+this.childrenNum.innerText.replace(/\s/g,"");
    this.elem.firstElementChild.textContent=resultStr;
    //隐藏dropDownContainer
    this.dropDownContainerHide();
  }
  documentClick(e){
    //避免事件冒泡
    if(e.target!==document.documentElement && e.target!==document.body) return;
    //隐藏dropDownContainer
    this.dropDownContainerHide();
  }
  dropDownContainerHide(){
    //div去掉聚集状态
    Utils.removeClass(this.elem,"focus");
    //dropDownContainer隐藏
    Utils.addClass(this.dropDownContainer,"none");
    //隐藏当前显示的ul列表
    if(this.listPrep) this.listPrep.style.display="none";
  }
  setDropDownList(type){
    //创建ul下拉列表内容
    let li="";
    let max=0;
    switch (type){
      case "adult": max=8;break;
      case "children": max=5;break;
      case "age": max=18;break;
    }
    for(let i=1;i<max;i++){
      li+="<li>"+i+"</li>";
    }
    return li;
  }
  static setStyles(){
    if(Main.styles) return;
    Main.style=true;
    Utils.insertCss(".guestsNum",{
      width:"108px",
      height:"34px",
      padding:"0px 12px",
      border:"1px solid #ccc",
      borderRadius:"3px",
      position:"relative",
      fontSize:"14px",
      color:"#666",
      userSelect:"none",
    })
    Utils.insertCss(".guestsNum.focus",{
      borderColor:"#ffa800",
      boxShadow:"0 0 4px #ffa800"
    })
    Utils.insertCss(".guestsNum>span",{
      lineHeight:"34px"
    })
    Utils.insertCss(".guestsNum>i",{
      display:"inline-block",
      width:"16px",
      height:"16px",
      backgroundImage:"url(./image/user.jpg)",
      float:"right",
      margin:"8px 0px 0px 10px"
    })
    Utils.insertCss(".dropDownContainer",{
      border: "1px solid #ffa800",
      borderRadius: "4px",
      boxShadow: "0 0 4px #ffa800",
      backgroundColor: "#fff",
      padding: "20px 15px",
      width: "480px",
      fontSize:"12px",
      position:"absolute",
      left:"0px",
      top:"35px",
    })
    Utils.insertCss(".dropDownItem",{
      marginBottom:"12px"
    })
    Utils.insertCss(".dropDownItem>span",{
      display:"block",
      width:"60px",
      lineHeight:"28px",
      float:"left",
    })
    Utils.insertCss(".dropDownSelect",{
      width:"90px",
      height:"30px",
      marginRight:"10px",
      float:"left",
      position:"relative"
    })
    Utils.insertCss(".dropDownCont",{
      border:"1px solid #ccc",
      borderRadius:"3px",
      height:"12px",
      padding:"6px 8px 10px",
    })
    Utils.insertCss(".dropDownCont>span",{
      display:"inline-block",
      width:"53px",
      height:"14px",
      lineHeight:"14px",
      borderRight:"1px solid #ccc"
    })
    Utils.insertCss(".dropDownCont>i",{
      display:"inline-block",
      width:"0px",
      height:"0px",
      border:"5px solid #c6c6c6",
      borderColor:"#c6c6c6 transparent transparent",
      margin: "6px 0px 0px 4px",
      float: "right"
    })
    Utils.insertCss(".dropDownList",{
      listStyle:"none",
      padding:"0px",
      margin:"0px",
      width:"88px",
      maxHeight:"200px",
      overflow:"auto",
      cursor:"pointer",
      border:"1px solid #ccc",
      backgroundColor:"#fff",
      borderRadius:"4px",
      position:"absolute",
      left:"0px",
      top:"30px",
      zIndex:"2",
      boxShadow: "1px 1px 3px rgba(0,0,0,.1)",
      display:"none"
    })
    Utils.insertCss(".dropDownList>li",{
      lineHeight:"28px",
      paddingLeft:"8px",
    })
    Utils.insertCss(".dropDownList>li:hover",{
      background:"#f4f4f4"
    })
    Utils.insertCss(".dropDownBottom",{
      borderTop:"1px solid #ccc",
      marginTop:"20px",
      paddingTop:"20px"
    })
    Utils.insertCss(".dropDownTips",{
      fontStyle:"normal",
      fontSize: "12px",
      color: "#ef523d",
      lineHeight:"28px"
    })
    Utils.insertCss(".dropDownBtn",{
      textDecoration:"none",
      float: "right",
      display: "inline-block",
      padding: "2px 22px",
      backgroundColor: "#ffb200",
      borderRadius: "4px",
      fontSize: "14px",
      lineHeight: "24px",
      color: "#fff",
    })
    Utils.insertCss(".dropDownBtn.disabled",{
      backgroundColor: "#efefef",
      color: "#999"
    })
    Utils.insertCss(".clearfix:after",{
      content:"\".\"",
      display:"block",
      overflow:"hidden",
      visibility:"hidden",
      clear:"both",
      height:"0px"
    })
    Utils.insertCss(".none",{
      display:"none"
    })
  }
}

Utils.js文件:

export default class Utils{
  static createE(elem,style,prep){
    elem=document.createElement(elem);
    if(style) for(let prop in style) elem.style[prop]=style[prop];
    if(prep) for(let prop in prep) elem[prop]=prep[prop];
    return elem;
  }
  static appendTo(elem,parent){
    if (parent.constructor === String) parent = document.querySelector(parent);
    parent.appendChild(elem);
  }
  static randomNum(min,max){
    return Math.floor(Math.random*(max-min)+min);
  }
  static randomColor(alpha){
    alpha=alpha||Math.random().toFixed(1);
    if(isNaN(alpha)) alpha=1;
    if(alpha>1) alpha=1;
    if(alpha<0) alpha=0;
    let col="rgba(";
    for(let i=0;i<3;i++){
      col+=Utils.randomNum(0,256)+",";
    }
    col+=alpha+")";
    return col;
  }
  static insertCss(select,styles){
    if(document.styleSheets.length===0){
      let styleS=Utils.createE("style");
      Utils.appendTo(styleS,document.head);
    }
    let styleSheet=document.styleSheets[document.styleSheets.length-1];
    let str=select+"{";
    for(var prop in styles){
      str+=prop.replace(/[A-Z]/g,function(item){
        return "-"+item.toLocaleLowerCase();
      })+":"+styles[prop]+";";
    }
    str+="}"
    styleSheet.insertRule(str,styleSheet.cssRules.length);
  }
  static getIdElem(elem,obj){
    if(elem.id) obj[elem.id]=elem;
    if(elem.children.length===0) return obj;
    for(let i=0;i<elem.children.length;i++){
      Utils.getIdElem(elem.children[i],obj);
    }
  }
  static addClass(elem,className){
    let arr=(elem.className+" "+className).match(/\S+/g);
    arr=arr.filter((item,index)=>arr.indexOf(item,index+1)<0)
    elem.className=arr.join(" ");
  }
  static removeClass(elem,className){
    if(!elem.className) return;
    let arr=elem.className.match(/\S+/g);
    let arr1=className.match(/\S+/g);
    arr1.forEach(item=>{
      arr=arr.filter(t=>t!==item)
    })
    elem.className=arr.join(" ");
  }
  static hasClass(elem,className){
    if(!elem.className) return false;
    let arr=elem.className.match(/\S+/g);
    let arr1=className.match(/\S+/g);
    let res;
    arr1.forEach(item=>{
      res= arr.some(it=>it===item)
    })
    return res;
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Jquery 点击按钮显示和隐藏层的代码
Jul 25 Javascript
javascript实现简单的二级联动
Mar 19 Javascript
jQuery实现信息提示框(带有圆角框与动画)效果
Aug 07 Javascript
Bootstrap基本插件学习笔记之折叠(22)
Dec 08 Javascript
jQuery点击头像上传并预览图片
Feb 23 Javascript
JavaScript实现兼容IE6的收起折叠与展开效果实例
Sep 20 Javascript
浅谈React Native Flexbox布局(小结)
Jan 08 Javascript
echarts设置图例颜色和地图底色的方法实例
Aug 01 Javascript
Bootstrap实现省市区三级联动(亲测可用)
Jul 26 Javascript
详解JavaScript修改注册表的方法
Jan 05 Javascript
js中位数不足自动补位扩展padLeft、padRight实现代码
Apr 06 Javascript
详解Vue Cli浏览器兼容性实践
Jun 08 Javascript
原生js实现自定义滚动条组件
Jan 20 #Javascript
原生js实现自定义滚动条
Jan 20 #Javascript
uniapp微信小程序:key失效的解决方法
Jan 20 #Javascript
JavaScript实现下拉列表
Jan 20 #Javascript
浅谈Vue开发人员的7个最好的VSCode扩展
Jan 20 #Vue.js
详解实现vue的数据响应式原理
Jan 20 #Vue.js
vue实现简易计算器功能
Jan 20 #Vue.js
You might like
数据库的日期格式转换
2006/10/09 PHP
php数组函数序列 之array_count_values() 统计数组中所有值出现的次数函数
2011/10/29 PHP
PHP Oauth授权和本地加密实现方法
2016/08/12 PHP
jQuery 源码分析笔记(7) Queue
2011/06/19 Javascript
JQuery获取浏览器窗口内容部分高度的代码
2012/02/24 Javascript
javascript一元操作符(递增、递减)使用示例
2013/08/07 Javascript
node.js中的console.time方法使用说明
2014/12/09 Javascript
浏览器检测JS代码(兼容目前各大主流浏览器)
2016/02/21 Javascript
微信小程序 wx.request(OBJECT)发起请求详解
2016/10/13 Javascript
javascript iframe跨域详解
2016/10/26 Javascript
Bootstrap源码解读导航条(7)
2016/12/23 Javascript
jQuery树插件zTree使用方法详解
2017/05/02 jQuery
node.js中http模块和url模块的简单介绍
2017/10/06 Javascript
js实现掷骰子小游戏
2019/10/24 Javascript
[03:55]2016国际邀请赛中国区预选赛首日TOP10精彩集锦
2016/06/27 DOTA
[49:20]VG vs TNC Supermajor小组赛B组败者组决赛 BO3 第二场 6.2
2018/06/03 DOTA
python根据文件大小打log日志
2014/10/09 Python
python循环监控远程端口的方法
2015/03/14 Python
Python如何获取系统iops示例代码
2016/09/06 Python
R vs. Python 数据分析中谁与争锋?
2017/10/18 Python
python assert的用处示例详解
2019/04/01 Python
树莓派使用python-librtmp实现rtmp推流h264的方法
2019/07/22 Python
双向RNN:bidirectional_dynamic_rnn()函数的使用详解
2020/01/20 Python
python标准库OS模块详解
2020/03/10 Python
Pycharm生成可执行文件.exe的实现方法
2020/06/02 Python
python调用私有属性的方法总结
2020/07/24 Python
一套软件测试笔试题
2014/07/25 面试题
应用电子专业学生的自我评价
2013/10/16 职场文书
个人简历自荐信
2013/12/05 职场文书
法律专业实习鉴定
2013/12/22 职场文书
超级搞笑检讨书
2014/01/15 职场文书
合作协议书
2014/04/23 职场文书
本科生自荐信
2014/06/18 职场文书
庆六一宣传标语
2014/10/08 职场文书
2014年机关党委工作总结
2014/12/11 职场文书
肖申克的救赎观后感
2015/06/02 职场文书