原生js實現(xiàn)下拉框選擇組件
本文實例為大家分享了js實現(xiàn)下拉框選擇組件的具體代碼,供大家參考,具體內(nèi)容如下
功能需求:
1、點擊div后,div顯示聚焦狀態(tài),同時顯示下拉框內(nèi)容;
2、選擇兒童人數(shù)后,如果兒童人數(shù)大于0,在下方出現(xiàn)對應的兒童年齡選擇框數(shù)量;
3、成人人數(shù)的選擇范圍是1-7,兒童人數(shù)的選擇范圍是0-4,兒童年齡的選擇范圍是<1、1-17;
4、點擊確認按鈕后,將選擇好的成人人數(shù)和兒童人數(shù)顯示在最上方的div內(nèi);
5、可以控制選擇框是否可點擊;
6、當顯示一個ul列表時,點擊另一個ul列表,將上一個ul列表隱藏;
7、點擊隱藏框內(nèi)除綁定事件元素外,將正在顯示的ul列表隱藏;
8、點擊頁面中任意空白位置,將顯示的下拉框內(nèi)容整體隱藏;
下拉框不可操作時的顯示狀態(tài):

下拉框可操作時:

選擇兒童人數(shù)后,下方自動出現(xiàn)對應數(shù)量的兒童年齡選擇框:

點擊確認按鈕后,將結果顯示在是上方的div內(nèi):

剛開始的想法是對select、ul下拉列表、btn按鈕分別進行事件監(jiān)聽,此外還要有當點擊下拉框內(nèi)其它位置時,ul下拉列表隱藏、當點擊body時整個下拉框內(nèi)容隱藏。監(jiān)聽事件過多,而且事件冒泡也會影響事件的執(zhí)行,導致某些事件會出現(xiàn)執(zhí)行多次的情況。
兒童年齡的選擇框是根據(jù)兒童的人數(shù)來生成的,有幾個兒童,就有幾個年齡選擇框。這種情況下,年齡的選擇框肯定是動態(tài)創(chuàng)建的,無法針對年齡的select進行事件監(jiān)聽,只能采用事件委托的形式,所以最后把對select、ul下拉列表、btn按鈕的點擊事件,還有當點擊container內(nèi)其它位置時,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';
//參數(shù)為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>人數(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">請優(yōu)先選擇日期,以便查詢實時價格。</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>`;
//設置樣式,因為樣式只設置一次就好,不需要實例化,所以使用靜態(tài)方法
Main.setStyles();
//獲取元素
Utils.getIdElem(div,this);
//監(jiān)聽div的點擊事件
div.addEventListener("click",(e)=>this.guestsNumClickHandler(e));
//如果state為true,下拉框監(jiān)聽點擊事件
if(this.state) this.dropDownContainer.addEventListener("click",e=>this.dropDownContainerClick(e));
//document監(jiān)聽點擊事件,隱藏下拉框
document.addEventListener("click",e=>this.documentClick(e));
return div;
}
appendTo(parent){
Utils.appendTo(this.elem,parent);
}
guestsNumClickHandler(e){
//如果下拉框是顯示狀態(tài),則直接跳出,避免重復操作
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作為參數(shù)
this.dropDownSelectClick(e.target.parentElement);
}
else if(Utils.hasClass(e.target,"dropDownCont")){
//點擊div選擇框時,將div作為參數(shù)
this.dropDownSelectClick(e.target);
}
else {
//點擊下拉框內(nèi)其它位置時,讓當前的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);
//根據(jù)li的數(shù)值,自動創(chuàng)建下面的年齡選擇框
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";
//循環(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");
//隱藏當前顯示的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;
}
}
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
JavaScript中l(wèi)ayim之整合右鍵菜單的示例代碼
這篇文章主要介紹了JavaScript中l(wèi)ayim之整合右鍵菜單的示例代碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02
HTML+JavaScript實現(xiàn)筋斗云導航欄效果
這篇文章主要為大家介紹了如何利用HTML+JavaScript+CSS實現(xiàn)筋斗云導航欄效果,當鼠標經(jīng)過某個li,筋斗云跟著到當前的位置,感興趣的小伙伴可以試一試2022-03-03
JavaScript實現(xiàn)微信紅包算法及問題解決方法
這篇文章主要介紹了JavaScript實現(xiàn)微信紅包算法及遇到的問題解決方法,需要的朋友可以參考下2018-04-04
微信小程序使用wx.navigateTo路由跳轉層級限制問題小結
在微信小程序開發(fā)中,wx.navigateTo和wx.redirectTo是兩種頁面跳轉方式,wx.navigateTo允許跳轉到新頁面并保留當前頁面,適合需要返回的場景,但受頁面棧10層限制,wx.redirectTo則關閉當前頁面后跳轉,本文介紹微信小程序使用wx.navigateTo路由跳轉層級限制問題2024-10-10
JavaScript實現(xiàn)重置表單(reset)的方法
這篇文章主要介紹了JavaScript實現(xiàn)重置表單(reset)的方法,涉及javascript中reset()方法的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-04-04

