原生js實(shí)現(xiàn)下拉框選擇組件
本文實(shí)例為大家分享了js實(shí)現(xiàn)下拉框選擇組件的具體代碼,供大家參考,具體內(nèi)容如下
功能需求:
1、點(diǎn)擊div后,div顯示聚焦?fàn)顟B(tài),同時(shí)顯示下拉框內(nèi)容;
2、選擇兒童人數(shù)后,如果兒童人數(shù)大于0,在下方出現(xiàn)對(duì)應(yīng)的兒童年齡選擇框數(shù)量;
3、成人人數(shù)的選擇范圍是1-7,兒童人數(shù)的選擇范圍是0-4,兒童年齡的選擇范圍是<1、1-17;
4、點(diǎn)擊確認(rèn)按鈕后,將選擇好的成人人數(shù)和兒童人數(shù)顯示在最上方的div內(nèi);
5、可以控制選擇框是否可點(diǎn)擊;
6、當(dāng)顯示一個(gè)ul列表時(shí),點(diǎn)擊另一個(gè)ul列表,將上一個(gè)ul列表隱藏;
7、點(diǎn)擊隱藏框內(nèi)除綁定事件元素外,將正在顯示的ul列表隱藏;
8、點(diǎn)擊頁面中任意空白位置,將顯示的下拉框內(nèi)容整體隱藏;
下拉框不可操作時(shí)的顯示狀態(tài):
下拉框可操作時(shí):
選擇兒童人數(shù)后,下方自動(dòng)出現(xiàn)對(duì)應(yīng)數(shù)量的兒童年齡選擇框:
點(diǎn)擊確認(rèn)按鈕后,將結(jié)果顯示在是上方的div內(nèi):
剛開始的想法是對(duì)select、ul下拉列表、btn按鈕分別進(jìn)行事件監(jiān)聽,此外還要有當(dāng)點(diǎn)擊下拉框內(nèi)其它位置時(shí),ul下拉列表隱藏、當(dāng)點(diǎn)擊body時(shí)整個(gè)下拉框內(nèi)容隱藏。監(jiān)聽事件過多,而且事件冒泡也會(huì)影響事件的執(zhí)行,導(dǎo)致某些事件會(huì)出現(xiàn)執(zhí)行多次的情況。
兒童年齡的選擇框是根據(jù)兒童的人數(shù)來生成的,有幾個(gè)兒童,就有幾個(gè)年齡選擇框。這種情況下,年齡的選擇框肯定是動(dòng)態(tài)創(chuàng)建的,無法針對(duì)年齡的select進(jìn)行事件監(jiān)聽,只能采用事件委托的形式,所以最后把對(duì)select、ul下拉列表、btn按鈕的點(diǎn)擊事件,還有當(dāng)點(diǎn)擊container內(nèi)其它位置時(shí),ul下拉列表隱藏。全部委托給了dropDownContainer元素。
下面附上代碼
html結(jié)構(gòu)代碼:
<!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'; //參數(shù)為false時(shí),選擇框不可點(diǎn)擊;為true時(shí),選擇框可使用 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控制下拉框是否可點(diǎn)擊 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>人數(shù)未定</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">請(qǐng)優(yōu)先選擇日期,以便查詢實(shí)時(shí)價(jià)格。</em>'} ${this.state?'<a class="dropDownBtn" id="dropDownBtn" href="javascript:void(0)" rel="external nofollow" rel="external nofollow" >確認(rèn)</a>':'<a class="dropDownBtn disabled" href="javascript:void(0)" rel="external nofollow" rel="external nofollow" >確認(rèn)</a>'} </div> </div>`; //設(shè)置樣式,因?yàn)闃邮街辉O(shè)置一次就好,不需要實(shí)例化,所以使用靜態(tài)方法 Main.setStyles(); //獲取元素 Utils.getIdElem(div,this); //監(jiān)聽div的點(diǎn)擊事件 div.addEventListener("click",(e)=>this.guestsNumClickHandler(e)); //如果state為true,下拉框監(jiān)聽點(diǎn)擊事件 if(this.state) this.dropDownContainer.addEventListener("click",e=>this.dropDownContainerClick(e)); //document監(jiān)聽點(diǎn)擊事件,隱藏下拉框 document.addEventListener("click",e=>this.documentClick(e)); return div; } appendTo(parent){ Utils.appendTo(this.elem,parent); } guestsNumClickHandler(e){ //如果下拉框是顯示狀態(tài),則直接跳出,避免重復(fù)操作 if(!Utils.hasClass(this.dropDownContainer,"none")) return; //如果點(diǎn)擊的不是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"){ //點(diǎn)擊ul選擇列表 this.dropDownListClick(e); } else if(e.target.id==="dropDownBtn"){ //點(diǎn)擊確認(rèn)按鈕 this.dropDownBtnClick(); } else if(e.target.nodeName==="SPAN" || e.target.nodeName==="I") { //點(diǎn)擊span或者i標(biāo)簽時(shí),將它們的父元素div作為參數(shù) this.dropDownSelectClick(e.target.parentElement); } else if(Utils.hasClass(e.target,"dropDownCont")){ //點(diǎn)擊div選擇框時(shí),將div作為參數(shù) this.dropDownSelectClick(e.target); } else { //點(diǎn)擊下拉框內(nèi)其它位置時(shí),讓當(dāng)前的ul列表隱藏 if(this.listPrep) this.listPrep.style.display="none"; } } dropDownSelectClick(div){ //隱藏掉上一個(gè)顯示的ul列表 if(this.listPrep) this.listPrep.style.display="none"; //當(dāng)前點(diǎn)擊的ul列表賦值給this.listPrep this.listPrep=div.nextElementSibling; //將當(dāng)前點(diǎn)擊的ul列表顯示 this.listPrep.style.display="block"; } dropDownListClick(e){ //獲取當(dāng)前點(diǎn)擊的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); //根據(jù)li的數(shù)值,自動(dòng)創(chuàng)建下面的年齡選擇框 this.setDropDownItemAge(txt); break; case "age": unit="歲";break; } //將選擇的li的值,顯示出來 this.listPrep.previousElementSibling.firstElementChild.textContent=e.target.innerText+" "+unit; //顯示完成后,將當(dāng)前顯示的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"; //循環(huán)選擇的數(shù)值,創(chuàng)建年齡選擇框 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(){ //將選擇的內(nèi)容顯示在最上方的select框內(nèi) 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去掉聚集狀態(tài) Utils.removeClass(this.elem,"focus"); //dropDownContainer隱藏 Utils.addClass(this.dropDownContainer,"none"); //隱藏當(dāng)前顯示的ul列表 if(this.listPrep) this.listPrep.style.display="none"; } setDropDownList(type){ //創(chuàng)建ul下拉列表內(nèi)容 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; } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JavaScript中l(wèi)ayim之整合右鍵菜單的示例代碼
這篇文章主要介紹了JavaScript中l(wèi)ayim之整合右鍵菜單的示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02HTML+JavaScript實(shí)現(xiàn)筋斗云導(dǎo)航欄效果
這篇文章主要為大家介紹了如何利用HTML+JavaScript+CSS實(shí)現(xiàn)筋斗云導(dǎo)航欄效果,當(dāng)鼠標(biāo)經(jīng)過某個(gè)li,筋斗云跟著到當(dāng)前的位置,感興趣的小伙伴可以試一試2022-03-03JavaScript實(shí)現(xiàn)微信紅包算法及問題解決方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)微信紅包算法及遇到的問題解決方法,需要的朋友可以參考下2018-04-04微信小程序使用wx.navigateTo路由跳轉(zhuǎn)層級(jí)限制問題小結(jié)
在微信小程序開發(fā)中,wx.navigateTo和wx.redirectTo是兩種頁面跳轉(zhuǎn)方式,wx.navigateTo允許跳轉(zhuǎn)到新頁面并保留當(dāng)前頁面,適合需要返回的場(chǎng)景,但受頁面棧10層限制,wx.redirectTo則關(guān)閉當(dāng)前頁面后跳轉(zhuǎn),本文介紹微信小程序使用wx.navigateTo路由跳轉(zhuǎn)層級(jí)限制問題2024-10-10JavaScript利用append添加元素報(bào)錯(cuò)的解決方法
這篇文章主要介紹了JavaScript利用append添加元素報(bào)錯(cuò)的解決方法,需要的朋友可以參考下2014-07-07JavaScript實(shí)現(xiàn)重置表單(reset)的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)重置表單(reset)的方法,涉及javascript中reset()方法的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04