1. 程式人生 > >原生js實現一個自定義下拉單選選擇框

原生js實現一個自定義下拉單選選擇框

  瀏覽器自帶的原生下拉框不太美觀,而且各個瀏覽器表現也不一致,UI一般給的下拉框也是和原生的下拉框差別比較大的,這就需要自己寫一個基本功能的下拉選單/下拉選擇框了。最近,把專案中用到的下拉框元件重新封裝了一下,以建構函式的方式進行封裝,主要方法和事件定義在原型上,下面是主要的實現程式碼並添加了比較詳細的註釋,分享出來供大家參考。程式碼用了ES6部分寫法如需相容低版本瀏覽器請把相關程式碼轉成es5寫法,或者直接bable轉下。

  先放個預覽圖吧,後面有最終的動態效果圖:(樣式和互動參考了阿里和Iview UI庫)

  

下面是主要的HTML程式碼(包含部分js呼叫程式碼):

<!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"> </
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>
View Code

頁面中定義了id為main的div,即為選擇框所要新增到的元素。傳入引數即可。

 接著就是樣式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;
}
View Code

樣式部分就不做什麼解釋了,下面放入壓軸的JS,同樣也無需過多解釋,註釋寫的很詳細了,上程式碼:

  1 /* jshint esversion: 6 */
  2 (function (window, document) {
  3     let Selector = function (option) {
  4         //執行初始化方法,
  5         this._init(option);
  6     };
  7 
  8     Selector.prototype = {
  9         //初始化傳入引數並定義初始化的相關變數
 10         _init({
 11             eleSelector = "", //傳入的選擇器 id,class,tag等,用於將選擇框渲染到此選擇器所在的元素
 12             options = [{
 13                 name: "請選擇",
 14                 value: "0",
 15             }], //傳入的下拉框物件,name為選擇的文字,value為值
 16             defaultText = "請選擇" //提供的預設選擇的值
 17         }) {
 18             
 19             //將傳入的資料繫結到this上
 20             this.parentEle = document.querySelector(eleSelector) || document.body; //要邦定的dom 
 21             this.options = options; //選擇值陣列物件
 22             this.defaultText = defaultText; //預設值
 23 
 24             this.dropboxShow = false; //定義儲存下拉框的顯示隱藏狀態
 25             this.defaultValue = ""; //定義村赤預設選中的值
 26             this._creatElement(); //初始化後執行建立元素方法
 27         },
 28 
 29         //建立下拉選擇框dom
 30         _creatElement() {
 31             //選擇框最外層的包裹元素
 32             let wrapEle = document.createElement("div");
 33             wrapEle.className = "my-select";
 34 
 35             //根據傳入的值獲取選擇框預設的值和內容
 36             this.options.forEach(item => {
 37                 if (item.name === "this.defaultText") {
 38                     this.defaultValue = item.value;
 39                 }
 40             });
 41 
 42             let selectWarpBox = document.createElement("div"); //選擇框包裹元素
 43             selectWarpBox.className = "select-selection";
 44 
 45             let inputHideBox = document.createElement("input"); //隱藏儲存選擇值得元素
 46             inputHideBox.type = "hidden";
 47             inputHideBox.value = this.defaultValue;
 48 
 49             let selectShowBox = document.createElement("div"); //選擇框預設展示框
 50             let selectNameBox = document.createElement("span"); //選擇框展現的值ele
 51             selectNameBox.className = "select-selected-value";
 52             selectNameBox.id = "select-option";
 53             selectNameBox.innerText = this.defaultText; //將傳入的預設值賦值
 54             let selectIcon = document.createElement("i"); //圖示ele
 55             selectIcon.className = "arrow-down icon-select-arrow";
 56             //將span和角標新增到外層div
 57             selectShowBox.appendChild(selectNameBox);
 58             selectShowBox.appendChild(selectIcon);
 59 
 60             selectWarpBox.appendChild(inputHideBox);
 61             selectWarpBox.appendChild(selectShowBox);
 62 
 63             //下拉框
 64             let dropbox = document.createElement("div"),
 65                 ulbox = document.createElement("ul");
 66 
 67             dropbox.id = "select-drop";
 68             dropbox.className = "select-dropdown";
 69             ulbox.className = "select-dropdown-list";
 70             //遍歷傳入的選項陣列物件,生成下拉選單的li元素並賦值
 71             this.options.forEach((item) => {
 72                 let itemLi = document.createElement("li");
 73                 if (this.defaultText === item.name) {
 74                     itemLi.className = "select-item select-item-selected";
 75                 } else {
 76                     itemLi.className = "select-item";
 77                 }
 78 
 79                 itemLi.setAttribute("data-value", item.value);
 80                 itemLi.innerText = item.name;
 81                 ulbox.appendChild(itemLi);
 82 
 83             });
 84             //將下拉框ul推入到包裹元素
 85             dropbox.appendChild(ulbox);
 86 
 87             wrapEle.appendChild(selectWarpBox);
 88             wrapEle.appendChild(dropbox);
 89 
 90             this.parentEle.appendChild(wrapEle); //將生成的下拉框新增到所選元素中
 91 
 92             //把需要操作的dom掛載到當前例項
 93             //this.wrapEle = wrapEle;     //最外層包裹元素
 94             this.eleSelect = selectWarpBox; //選擇框
 95             this.eleDrop = dropbox; //下拉框
 96             this.eleSpan = selectNameBox; //顯示文字的span節點
 97 
 98             //繫結事件處理函式
 99             this._bind(this.parentEle);
100         },
101 
102         //點選下拉框事件處理函式
103         _selectHandleClick() {
104             if (this.dropboxShow) {
105                 this._selectDropup();
106             } else {
107                 this._selectDropdown();
108             }
109         },
110 
111         //收起下拉選項
112         _selectDropup() {
113             this.eleDrop.style.transform = "scale(1,0)";
114             this.eleDrop.style.opacity = "0";
115             this.eleSelect.className = "select-selection";
116             this.dropboxShow = false;
117         },
118 
119         //展示下拉選項
120         _selectDropdown() {
121             this.eleDrop.style.transform = "scale(1,1)";
122             this.eleDrop.style.opacity = "1";
123             this.eleSelect.className = "select-selection select-focus";
124             this.dropboxShow = true;
125         },
126 
127         //點選下拉選項進行賦值
128         _dropItemClick(ele) {
129             this.defaultValue = ele.getAttribute("data-value");
130             //document.querySelector("#select-value").value = ele.getAttribute("data-value");
131             this.eleSpan.innerText = ele.innerText;
132             ele.className = "select-item select-item-selected";
133             //對點選選中的其他所有兄弟元素修改class去除選中樣式
134             this._siblingsDo(ele, function (ele) {
135                 if (ele) {
136                     ele.className = "select-item";
137                 }
138             });
139             this._selectDropup();
140         },
141 
142         //node遍歷是否是子元素包裹元素
143         _getTargetNode(ele, target) {
144             //ele是內部元素,target是你想找到的包裹元素
145             if (!ele || ele === document) return false;
146             return ele === target ? true : this._getTargetNode(ele.parentNode, target);
147         },
148 
149         //兄弟元素遍歷處理函式
150         _siblingsDo(ele, fn) {
151 
152             (function (ele) {
153                 fn(ele);
154                 if (ele && ele.previousSibling) {
155                     arguments.callee(ele.previousSibling);
156                 }
157             })(ele.previousSibling);
158 
159             (function (ele) {
160                 fn(ele);
161                 if (ele && ele.nextSibling) {
162                     arguments.callee(ele.nextSibling);
163                 }
164             })(ele.nextSibling);
165 
166         },
167 
168         //繫結下拉框事件處理函式
169         _bind(parentEle) {
170             let _this = this;
171             //事件委託到最外層包裹元素進行繫結處理
172             parentEle.addEventListener("click", function (e) {
173                 const ele = e.target;
174                 
175                 //遍歷當前點選的元素,如果是選中框內的元素執行
176                 if (_this._getTargetNode(ele, _this.eleSelect)) {
177                     if (_this.dropboxShow) {
178                         _this._selectDropup();
179                     } else {
180                         _this._selectDropdown();
181                     }
182                 } else if (ele.className === "select-item") { //如果是點選的下拉框的選項執行
183                     _this._dropItemClick(ele);
184                 } else { //點選其他地方隱藏下拉框
185                     _this._selectDropup();
186                 }
187 
188             });
189 
190         }
191 
192     };
193     //將建構函式掛載到全域性window
194     window.$Selector = Selector;
195 })(window, document);
View Code

到此,一個自定義下拉選單就出爐了,下面是動態效果:

至此,從css自定義的表單元素到下拉框元素都已經自定義完畢,使用bootstrap的同學把這些加進去就能基本保持各瀏覽器效果一致性和美觀性了,就到這吧先。