原生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 相关文章推荐
自己实现string的substring方法 人民币小写转大写,数字反转,正则优化
Sep 02 Javascript
javascript 中String.match()与RegExp.exec()的区别说明
Jan 10 Javascript
javascript贪吃蛇完整版(源码)
Dec 09 Javascript
JavaScript两种跨域技术全面介绍
Apr 16 Javascript
在JavaScript应用中实现延迟加载的方法
Jun 25 Javascript
js实现页面跳转的五种方法推荐
Mar 10 Javascript
深入浅析JavaScript中的constructor
Apr 19 Javascript
jquery html5 视频播放控制代码
Nov 06 Javascript
jquery实现表单获取短信验证码代码
Mar 13 Javascript
Javascript调试之console对象——你不知道的一些小技巧
Jul 10 Javascript
vue自定义移动端touch事件之点击、滑动、长按事件
Jul 10 Javascript
VUE v-model表单数据双向绑定完整示例
Jan 21 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
PHP中的日期处理方法集锦
2007/01/02 PHP
php array_search() 函数使用
2010/04/13 PHP
允许phpmyadmin空密码登录的配置方法
2011/05/29 PHP
typecho插件编写教程(三):保存配置
2015/05/28 PHP
php+ajax无刷新上传图片的实现方法
2016/12/06 PHP
js实现ASP分页函数 HTML分页函数
2006/09/22 Javascript
jquery实现点击页面计算点击次数
2015/01/23 Javascript
js禁止页面刷新与后退的方法
2015/06/08 Javascript
Jquery实现遮罩层的方法
2015/06/08 Javascript
jquery实现表单输入时提示文字滑动向上效果
2015/08/10 Javascript
jquery实现Li滚动时滚动条自动添加样式的方法
2015/08/10 Javascript
Jquery ajax基础教程
2015/11/20 Javascript
jquery利用拖拽方式在图片上添加热链接
2015/11/24 Javascript
使用Javascript监控前端相关数据的代码
2016/10/27 Javascript
webpack4简单入门实例
2018/09/06 Javascript
使用FormData实现上传多个文件
2018/12/04 Javascript
vue之debounce属性被移除及处理详解
2019/11/13 Javascript
Python中is与==判断的区别
2017/03/28 Python
python技能之数据导出excel的实例代码
2017/08/11 Python
python 巧用正则寻找字符串中的特定字符的位置方法
2018/05/02 Python
python实现dijkstra最短路由算法
2019/01/17 Python
python使用phoenixdb操作hbase的方法示例
2019/02/28 Python
python的reverse函数翻转结果为None的问题
2020/05/11 Python
Pytorch 图像变换函数集合小结
2021/02/01 Python
python 如何在测试中使用 Mock
2021/03/01 Python
伦敦剧院及景点门票:Encore Tickets
2018/07/01 全球购物
Under Armour西班牙官网:美国知名的高端功能性运动品牌
2018/12/12 全球购物
高中数学教师求职信
2013/10/30 职场文书
公司捐款倡议书
2014/05/14 职场文书
欢迎领导检查标语
2014/06/27 职场文书
人大调研汇报材料
2014/08/14 职场文书
文明单位申报材料
2014/12/23 职场文书
保送生自荐信范文
2015/03/26 职场文书
团拜会主持词
2015/07/04 职场文书
《草船借箭》教学反思
2016/02/23 职场文书
RestTemplate如何通过HTTP Basic Auth认证示例说明
2022/03/17 Java/Android