原生JS實現(xiàn)自定義下拉單選選擇框功能
瀏覽器自帶的原生下拉框不太美觀,而且各個瀏覽器表現(xiàn)也不一致,UI一般給的下拉框也是和原生的下拉框差別比較大的,這就需要自己寫一個基本功能的下拉菜單/下拉選擇框了。最近,把項目中用到的下拉框組件重新封裝了一下,以構(gòu)造函數(shù)的方式進行封裝,主要方法和事件定義在原型上,下面是主要的實現(xiàn)代碼并添加了比較詳細的注釋,分享出來供大家參考。代碼用了ES6部分寫法如需兼容低版本瀏覽器請把相關(guān)代碼轉(zhuǎn)成es5寫法,或者直接bable轉(zhuǎn)下。
先放個預(yù)覽圖吧,后面有最終的動態(tài)效果圖:(樣式和交互參考了阿里和Iview UI庫)

下面是主要的HTML代碼(包含部分js調(diào)用代碼):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Diy Select</title>
<link rel="stylesheet" href="index.css" rel="external nofollow" >
</head>
<body>
<div id="main" class="main"></div>
<script src="index.js"></script>
<script>
document.addEventListener("DOMContentLoaded",function(){
const select1 = new $Selector({
eleSelector:"#main",
options:[
{name:"選項1",value:"0"},
{name:"選項2",value:"1"},
{name:"選項3",value:"2"}
],
defaultText:"選項2"
});
})
</script>
</body>
</html>
頁面中定義了id為main的div,即為選擇框所要添加到的元素。傳入?yún)?shù)即可。
接著就是樣式CSS部分了,沒啥說的了,手動滑稽:
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.main {
padding: 40px;
}
.my-select {
display: inline-block;
width: auto;
min-width: 80px;
box-sizing: border-box;
vertical-align: middle;
color: #515a6e;
font-size: 14px;
line-height: normal;
position: relative;
}
.select-selection {
display: block;
box-sizing: border-box;
outline: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: pointer;
position: relative;
background-color: #fff;
border-radius: 4px;
border: 1px solid #dcdee2;
transition: all .2s ease-in-out;
}
.select-selection:hover,
.select-selection.select-focus {
border-color: #57a3f3;
box-shadow: 0 0 0 2px rgba(45, 140, 240, .2);
}
.select-selected-value {
display: block;
height: 28px;
line-height: 28px;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-left: 8px;
padding-right: 24px;
}
.icon-select-arrow {
position: absolute;
top: 50%;
right: 8px;
line-height: 1;
margin-top: -7px;
font-size: 14px;
color: #808695;
transition: all .2s ease-in-out;
display: inline-block;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
text-rendering: auto;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
vertical-align: middle;
}
.icon-select-arrow::before {
content: "";
display: block;
width: 6px;
height: 6px;
background-color: transparent;
border-left: 1.5px solid #808695;
border-bottom: 1.5px solid #808695;
transform: rotate(-45deg);
}
.select-dropdown {
width: auto;
min-width: 80px;
max-height: 200px;
overflow: auto;
margin: 5px 0;
padding: 5px 0;
background-color: #fff;
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
position: absolute;
z-index: 2;
transform-origin: center top 0px;
transition: all 0.3s;
will-change: top, left;
top: 30px;
left: 0;
transform: scale(1, 0);
opacity: 0;
}
.select-item {
line-height: normal;
padding: 7px 16px;
clear: both;
color: #515a6e;
font-size: 12px !important;
white-space: nowrap;
list-style: none;
cursor: pointer;
transition: background .2s ease-in-out;
}
.select-item.select-item-selected,
.select-item:hover {
color: #2d8cf0;
background-color: #f3f3f3;
}
樣式部分就不做什么解釋了,下面放入壓軸的JS,同樣也無需過多解釋,注釋寫的很詳細了,上代碼:
/* jshint esversion: 6 */
(function (window, document) {
let Selector = function (option) {
//執(zhí)行初始化方法,
this._init(option);
};
Selector.prototype = {
//初始化傳入?yún)?shù)并定義初始化的相關(guān)變量
_init({
eleSelector = "", //傳入的選擇器 id,class,tag等,用于將選擇框渲染到此選擇器所在的元素
options = [{
name: "請選擇",
value: "0",
}], //傳入的下拉框?qū)ο螅琻ame為選擇的文字,value為值
defaultText = "請選擇" //提供的默認選擇的值
}) {
//將傳入的數(shù)據(jù)綁定到this上
this.parentEle = document.querySelector(eleSelector) || document.body; //要邦定的dom
this.options = options; //選擇值數(shù)組對象
this.defaultText = defaultText; //默認值
this.dropboxShow = false; //定義存儲下拉框的顯示隱藏狀態(tài)
this.defaultValue = ""; //定義村赤默認選中的值
this._creatElement(); //初始化后執(zhí)行創(chuàng)建元素方法
},
//創(chuàng)建下拉選擇框dom
_creatElement() {
//選擇框最外層的包裹元素
let wrapEle = document.createElement("div");
wrapEle.className = "my-select";
//根據(jù)傳入的值獲取選擇框默認的值和內(nèi)容
this.options.forEach(item => {
if (item.name === "this.defaultText") {
this.defaultValue = item.value;
}
});
let selectWarpBox = document.createElement("div"); //選擇框包裹元素
selectWarpBox.className = "select-selection";
let inputHideBox = document.createElement("input"); //隱藏保存選擇值得元素
inputHideBox.type = "hidden";
inputHideBox.value = this.defaultValue;
let selectShowBox = document.createElement("div"); //選擇框默認展示框
let selectNameBox = document.createElement("span"); //選擇框展現(xiàn)的值ele
selectNameBox.className = "select-selected-value";
selectNameBox.id = "select-option";
selectNameBox.innerText = this.defaultText; //將傳入的默認值賦值
let selectIcon = document.createElement("i"); //圖標ele
selectIcon.className = "arrow-down icon-select-arrow";
//將span和角標添加到外層div
selectShowBox.appendChild(selectNameBox);
selectShowBox.appendChild(selectIcon);
selectWarpBox.appendChild(inputHideBox);
selectWarpBox.appendChild(selectShowBox);
//下拉框
let dropbox = document.createElement("div"),
ulbox = document.createElement("ul");
dropbox.id = "select-drop";
dropbox.className = "select-dropdown";
ulbox.className = "select-dropdown-list";
//遍歷傳入的選項數(shù)組對象,生成下拉菜單的li元素并賦值
this.options.forEach((item) => {
let itemLi = document.createElement("li");
if (this.defaultText === item.name) {
itemLi.className = "select-item select-item-selected";
} else {
itemLi.className = "select-item";
}
itemLi.setAttribute("data-value", item.value);
itemLi.innerText = item.name;
ulbox.appendChild(itemLi);
});
//將下拉框ul推入到包裹元素
dropbox.appendChild(ulbox);
wrapEle.appendChild(selectWarpBox);
wrapEle.appendChild(dropbox);
this.parentEle.appendChild(wrapEle); //將生成的下拉框添加到所選元素中
//把需要操作的dom掛載到當(dāng)前實例
//this.wrapEle = wrapEle; //最外層包裹元素
this.eleSelect = selectWarpBox; //選擇框
this.eleDrop = dropbox; //下拉框
this.eleSpan = selectNameBox; //顯示文字的span節(jié)點
//綁定事件處理函數(shù)
this._bind(this.parentEle);
},
//點擊下拉框事件處理函數(shù)
_selectHandleClick() {
if (this.dropboxShow) {
this._selectDropup();
} else {
this._selectDropdown();
}
},
//收起下拉選項
_selectDropup() {
this.eleDrop.style.transform = "scale(1,0)";
this.eleDrop.style.opacity = "0";
this.eleSelect.className = "select-selection";
this.dropboxShow = false;
},
//展示下拉選項
_selectDropdown() {
this.eleDrop.style.transform = "scale(1,1)";
this.eleDrop.style.opacity = "1";
this.eleSelect.className = "select-selection select-focus";
this.dropboxShow = true;
},
//點擊下拉選項進行賦值
_dropItemClick(ele) {
this.defaultValue = ele.getAttribute("data-value");
//document.querySelector("#select-value").value = ele.getAttribute("data-value");
this.eleSpan.innerText = ele.innerText;
ele.className = "select-item select-item-selected";
//對點擊選中的其他所有兄弟元素修改class去除選中樣式
this._siblingsDo(ele, function (ele) {
if (ele) {
ele.className = "select-item";
}
});
this._selectDropup();
},
//node遍歷是否是子元素包裹元素
_getTargetNode(ele, target) {
//ele是內(nèi)部元素,target是你想找到的包裹元素
if (!ele || ele === document) return false;
return ele === target ? true : this._getTargetNode(ele.parentNode, target);
},
//兄弟元素遍歷處理函數(shù)
_siblingsDo(ele, fn) {
(function (ele) {
fn(ele);
if (ele && ele.previousSibling) {
arguments.callee(ele.previousSibling);
}
})(ele.previousSibling);
(function (ele) {
fn(ele);
if (ele && ele.nextSibling) {
arguments.callee(ele.nextSibling);
}
})(ele.nextSibling);
},
//綁定下拉框事件處理函數(shù)
_bind(parentEle) {
let _this = this;
//事件委托到最外層包裹元素進行綁定處理
parentEle.addEventListener("click", function (e) {
const ele = e.target;
//遍歷當(dāng)前點擊的元素,如果是選中框內(nèi)的元素執(zhí)行
if (_this._getTargetNode(ele, _this.eleSelect)) {
if (_this.dropboxShow) {
_this._selectDropup();
} else {
_this._selectDropdown();
}
} else if (ele.className === "select-item") { //如果是點擊的下拉框的選項執(zhí)行
_this._dropItemClick(ele);
} else { //點擊其他地方隱藏下拉框
_this._selectDropup();
}
});
}
};
//將構(gòu)造函數(shù)掛載到全局window
window.$Selector = Selector;
})(window, document);
到此,一個自定義下拉菜單就出爐了,下面是動態(tài)效果:

至此,從css自定義的表單元素到下拉框元素都已經(jīng)自定義完畢,使用bootstrap的同學(xué)把這些加進去就能基本保持各瀏覽器效果一致性和美觀性了,就到這吧先。
推薦:
感興趣的朋友可以關(guān)注小編的微信公眾號【碼農(nóng)那點事兒】,更多網(wǎng)頁制作特效源碼及學(xué)習(xí)干貨哦?。。?/p>
總結(jié)
以上所述是小編給大家介紹的原生js實現(xiàn)一個自定義下拉單選選擇框功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
輕松玩轉(zhuǎn)BootstrapTable(后端使用SpringMVC+Hibernate)
這篇文章主要和大家輕松玩轉(zhuǎn)BootstrapTable,后端使用SpringMVC+Hibernate,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09
解決IE下select標簽innerHTML插入option的BUG(兼容IE,FF,Opera,Chrome,Safa
在ie下面使用innerHTML來插入option選項的話,ie會去掉前面的<option>,并拆分成多個節(jié)點,這樣會造成select的出錯2010-05-05
js遍歷獲取表格內(nèi)數(shù)據(jù)的方法(必看)
下面小編就為大家?guī)硪黄猨s遍歷獲取表格內(nèi)數(shù)據(jù)的方法(必看)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04
JavaScript在IE中“意外地調(diào)用了方法或?qū)傩栽L問”
FF是正常的,IE報“意外地調(diào)用了方法或?qū)傩栽L問”。2008-11-11
JS寫XSS cookie stealer來竊取密碼的步驟詳解
JavaScript是web中最常用的腳本開發(fā)語言,js可以自動執(zhí)行站點組件,管理站點內(nèi)容,在web業(yè)內(nèi)實現(xiàn)其他有用的函數(shù)。這篇文章主要介紹了JS寫XSS cookie stealer來竊取密碼的步驟詳解,需要的朋友可以參考下2017-11-11
js跨域問題之跨域iframe自適應(yīng)大小實現(xiàn)代碼
前幾天做公司和開心網(wǎng)合作項目的時候 碰到iframe 跨域自適應(yīng)的問題剛開始很迷惑 開心網(wǎng)那邊技術(shù)工程師給我發(fā)了一段這樣子的代碼。2010-07-07

