原生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 相关文章推荐
浅谈重写window对象的方法
Dec 29 Javascript
transport.js和jquery冲突问题的解决方法
Feb 10 Javascript
JS实现左右拖动改变内容显示区域大小的方法
Oct 13 Javascript
jQuery实现页面评论栏中访客信息自动填写功能的方法
May 23 Javascript
React router动态加载组件之适配器模式的应用详解
Sep 12 Javascript
Vue中使用create-keyframe-animation与动画钩子完成复杂动画
Apr 09 Javascript
Vue中使用matomo进行访问流量统计的实现
Nov 05 Javascript
jquery实现直播视频弹幕效果
Feb 25 jQuery
js实现上传按钮并显示缩略图小轮子
May 04 Javascript
JavaScript JSON使用原理及注意事项
Jul 30 Javascript
解决vue prop传值default属性如何使用,为何不生效的问题
Sep 21 Javascript
多种类型jQuery网页验证码插件代码实例
Jan 09 jQuery
原生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中获取内网用户MAC地址(WINDOWS/linux)的实现代码
2011/08/11 PHP
PHP+MYSQL会员系统的登陆即权限判断实现代码
2011/09/23 PHP
php自定文件保存session的方法
2014/12/10 PHP
php递归删除指定文件夹的方法小结
2015/04/20 PHP
ThinkPHP开发框架函数详解:C方法
2015/08/14 PHP
PHP封装函数实现生成随机的字符串验证码
2017/01/24 PHP
新手常遇到的一些jquery问题整理
2010/08/16 Javascript
利用JQuery和JS实现奇偶行背景颜色自定义效果
2012/11/19 Javascript
页面右下角弹出提示框示例代码js版
2013/08/02 Javascript
JS+DIV实现鼠标划过切换层效果的实例代码
2013/11/26 Javascript
用html+css+js实现的一个简单的图片切换特效
2014/05/28 Javascript
JavaScript实现的in_array函数
2014/08/27 Javascript
浅谈JS中的bind方法与函数柯里化
2016/08/10 Javascript
有关suggest快速删除后仍然出现下拉列表的bug问题
2016/12/02 Javascript
JavaScript比较两个数组的内容是否相同(推荐)
2017/05/02 Javascript
基于vue的换肤功能的示例代码
2017/10/10 Javascript
AngularJS 事件发布机制
2018/08/28 Javascript
微信小程序http连接访问解决方案的示例
2018/11/05 Javascript
Vue-cli3项目引入Typescript的实现方法
2019/10/18 Javascript
React.js组件实现拖拽排序组件功能过程解析
2020/04/27 Javascript
[00:15]TI9地铁玩家打卡
2019/08/11 DOTA
python使用sqlite3时游标使用方法
2018/03/13 Python
python 已知一个字符,在一个list中找出近似值或相似值实现模糊匹配
2020/02/29 Python
python利用os模块编写文件复制功能——copy()函数用法
2020/07/13 Python
加拿大城市本地限时优惠:Buytopia.ca
2018/09/19 全球购物
C#如何调用Windows程序打开一个文档
2014/12/26 面试题
2014年应届大学生自我评价
2014/01/09 职场文书
旷课检讨书2000字
2014/01/14 职场文书
信息专业毕业生五年职业规划参考
2014/02/06 职场文书
工商管理自荐书
2014/07/06 职场文书
文明好少年事迹材料
2014/08/19 职场文书
八项规定自查自纠报告及整改措施
2014/10/26 职场文书
单身申明具结书
2015/02/26 职场文书
风之谷观后感
2015/06/11 职场文书
Python 数据可视化之Matplotlib详解
2021/11/02 Python
Java 异步任务计算FutureTask
2022/04/28 Java/Android