Native js implementation drop-down box selection component development

Drop-down box selection

Functional requirements:

  1. After clicking div, the div shows the aggregation state and the contents of the hidden box.
  2. After selecting the number of children, if the number of children is greater than 0, the corresponding number of child age selection boxes appears below.
  3. The number of adults is 1-7, the number of children is 0-4, and the age of children is < 1, 1-17.
  4. After clicking the confirmation button, the number of selected adults and children is displayed in the top div;
  5. You can control whether the selection box is clickable or not;
  6. When one ul list is displayed, click on another ul list to hide the previous ul list automatically;
  7. Click inside the Hide box to hide the displayed ul list except for the bound event elements;
  8. Click on any blank location on the page to hide the contents of the drop-down box displayed as a whole;

Take a look at the effect:
When the drop-down box is not operable:
When the drop-down box is operational:
After selecting the number of children, the corresponding number of children's age selection boxes automatically appear below:

The initial idea was to listen for events on select, ul drop-down list, btn buttons, and hide the ul drop-down list when you click somewhere else in the drop-down box, or the entire drop-down box when you click body.There are too many listening events, and event bubbling can affect the execution of events, resulting in some events being executed more than once.

The selection box for children's age is generated based on the number of children, with several children and several age selection boxes.In this case, the selection box for age must be created dynamically, and event monitoring for age selects is not possible. It can only be done in the form of event delegation, so the ul drop-down list, the btn button click event, and the ul drop-down list is hidden when clicking elsewhere in the container.All delegated to dropDownContainer element.

Attach code below

html structure code:

<!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';
        //When the parameter is false, the selection box is not clickable; when true, the selection box can be used
        let main=new Main(true);
        main.appendTo("body");
    </script>
</body>
</html>

Main.js file:

import Utils from './Utils.js';
export default class Main{
    static styles=false;
    listPrep;
    constructor(state){
        //state controls whether the drop-down box is clickable
        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>Indeterminate number</span><i></i>
        <div class="dropDownContainer none" id="dropDownContainer">
            <div class="dropDownItem clearfix">
                <span>Every room</span>
                <div class="dropDownSelect">
                    <div class="dropDownCont"><span id="adultNum">2 Adult</span><i></i></div>
                    <ul class="dropDownList" tag="adult">${this.setDropDownList("adult")}</ul>
                </div>
                <div class="dropDownSelect">
                    <div class="dropDownCont"><span id="childrenNum">0 children</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">Please select the date first in order to query the real-time price.</em>'}
                ${this.state?'<a class="dropDownBtn" id="dropDownBtn" href="javascript:void(0)">confirm</a>':'<a class="dropDownBtn disabled" href="javascript:void(0)">confirm</a>'}
            </div>
        </div>`;
        //Set Style
        this.setStyles();
        //Get Elements
        Utils.getIdElem(div,this);
        div.addEventListener("click",(e)=>this.guestsNumClickHandler(e));
        //If the state is true, the drop-down box listens for click events
        if(this.state) this.dropDownContainer.addEventListener("click",e=>this.dropDownContainerClick(e));
        //document listens for click events and hides drop-down boxes
        document.addEventListener("click",e=>this.documentClick(e));
        return div;
    }
    appendTo(parent){
        Utils.appendTo(this.elem,parent);
    }
    guestsNumClickHandler(e){
        //If the drop-down box is in the display state, jump out directly to avoid duplicate operations
        if(!Utils.hasClass(this.dropDownContainer,"none")) return;
        //If you click on something other than guestsNum, jump out and avoid bubble
        if(e.target.nodeName!=="SPAN"&&e.target.nodeName!=="I"&&!Utils.hasClass(e.target,"guestsNum")) return;
        //Add Aggregation Style to Divide
        Utils.addClass(this.elem,"focus");
        //Show dropDownContainer
        Utils.removeClass(this.dropDownContainer,"none");
    }
    dropDownContainerClick(e){
        if(e.target.nodeName==="LI"){
            //Click ul to select list
            this.dropDownListClick(e);
        }
        else if(e.target.id==="dropDownBtn"){
            //Click the Confirm button
            this.dropDownBtnClick();
        }
        else if(e.target.nodeName==="SPAN" || e.target.nodeName==="I") {
            //When clicking on the span or i tags, take their parent div as a parameter
            this.dropDownSelectClick(e.target.parentElement);
        }
        else if(Utils.hasClass(e.target,"dropDownCont")){
            //When clicking on the div selection box, take the div as a parameter
            this.dropDownSelectClick(e.target);
        }
        else {
            //Hide the current ul list when clicking elsewhere in the drop-down box
            if(this.listPrep) this.listPrep.style.display="none";
        }
    }
    dropDownSelectClick(div){
        //Hide the last displayed ul list
        if(this.listPrep) this.listPrep.style.display="none";
        //The ul list currently clicked is assigned to this.listPrep
        this.listPrep=div.nextElementSibling;
        //Display the list of ULS currently clicked
        this.listPrep.style.display="block";
    }
    dropDownListClick(e){
        //Gets the tag attribute value of the ul currently clicked
        let tag=this.listPrep.getAttribute("tag");
        let unit="";
        switch (tag){
            case "adult": unit="Adult";break;
            case "children": 
                unit="children";
                let txt=Number(e.target.innerText);
                //Automatically create the following age selection box based on the value of li
                this.setDropDownItemAge(txt);
                break;
            case "age": unit="year";break;
        }
        //Display the value of the selected li
        this.listPrep.previousElementSibling.firstElementChild.textContent=e.target.innerText+" "+unit;
        //Hide the currently displayed ul when the display is complete
        this.listPrep.style.display="none";
    }
    setDropDownItemAge(txt){
        let str="<span>Child Age</span>";
        if(txt===0){
            //If 0, the age selection box does not appear
            this.ItemAge.style.display="none";
        }else{
            this.ItemAge.style.display="block";
            //Circular selection of values to create an age selection box
            for(let i=0;i<txt;i++){
                str+=`<div class="dropDownSelect">
                <div class="dropDownCont"><span><1 year</span><i></i></div>
                <ul class="dropDownList" tag="age"><li><1</li>${this.setDropDownList("age")}</ul>
            </div>`;
            }
            this.ItemAge.innerHTML=str;
        }
    }
    dropDownBtnClick(){
        //Show the selection in the top select box
        let resultStr=this.adultNum.innerText.replace(/\s/g,"")+" "+this.childrenNum.innerText.replace(/\s/g,"");
        this.elem.firstElementChild.textContent=resultStr;
        //Hide dropDownContainer
        this.dropDownContainerHide();
    }
    
    documentClick(e){
        //Avoid Event Bubbles
        if(e.target!==document.documentElement && e.target!==document.body) return;
        //Hide dropDownContainer
        this.dropDownContainerHide();
    }
    dropDownContainerHide(){
        //div removes aggregation
        Utils.removeClass(this.elem,"focus");
        //dropDownContainer Hide
        Utils.addClass(this.dropDownContainer,"none");
        //Hide the currently displayed ul list
        if(this.listPrep) this.listPrep.style.display="none";
    }
    setDropDownList(type){
        //Create ul drop-down list content
        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;
    }
    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 file:

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;
    }
}
62 original articles published. 29. 20,000 visits+
Private letter follow

Tags: Javascript Attribute

Posted on Wed, 29 Jan 2020 18:31:10 -0800 by XaeroDegreaz