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

本文实例为大家分享了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;
  }
}

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容