純原生javascript下拉框表單美化例項教程

蔣偉平發表於2021-01-18

html的表單有很強大的功能,在web早期的時候,表單是頁面向伺服器發起通訊的主要渠道。但有些表單元素的樣式沒辦法通過新增css樣式來達到滿意的效果,而且不同的瀏覽器之間設定的樣式還存在相容問題,比如下拉框。

本例項通過建立div和li等元素來生成一個模擬下拉框,以達到美化下拉框的效果。學習本教程之前,讀者需要具備html和css技能,同時需要有簡單的javascript基礎。

先建立一個select元素,作為美化下拉框的資料來源,如下所示:

<div class="select_wrap" id="selectWrap">
  <dl>
    <dt>請選擇:</dt>
    <dd>
      <select id="selectElem">
        <option value="1">北京</option>
        <option value="2">上海</option>
        <option value="3">廣東</option>
        <option value="4">湖南</option>
        <option value="5">河北</option>
        <option value="6">黑龍江</option>
      </select>
    </dd>
  </dl>
</div>

 

既然是做一個下拉框美化的效果,那肯定是需要用一些css樣式來實現。讀者可以根據自己有喜好編寫,也可以直接複製以下程式碼:

.select_wrap{
  width:800px;
  margin:30px auto;
}
.select_wrap dt{
  float:left;
  width:120px;
  line-height:36px;
  text-align:right;
  font-size:14px;
}
.select_wrap dd{
  margin-left:130px;
  line-height:36px;
}
.select_wrap input[type=text],.select_wrap input[type=password]{
  height:24px;
  line-height:22px;
  padding:0 5px;
  border:1px solid #aaa;
  border-radius:2px;
}
.select_wrap .btn{
  padding:0 20px;
  color:#fff;
  cursor:pointer;
  line-height:30px;
  border:none;
  margin-right:20px;
  background:#108ee9;
}
.select_container{
  position:relative;
  display:inline-block;
}
.input_container:{
  position:relative;
}
.input_container::after{
  content:"";
  position:absolute;
  top:15px;
  right:8px;
  display:inline-block; 
  height:0px;
  border:6px solid transparent;
  border-top-color:#ccc;
  pointer-events:none;
}
.input_container input{
  height:30px;
  line-height:28px;
  padding:0 5px;
  border:1px solid #aaa;
  border-radius:4px;
}
.input_container input:focus{
  border-color:#129cff;
  outline:none;
  box-shadow:0 0 6px #65bfff;
}
.select_container ul{
  position:absolute;
  top:35px;
  width:100%;
  margin:0;
  padding:0;
  background:#fff;
  border-radius:4px;
  box-shadow:0 0px 5px #ccc;
}
.select_container li{
  list-style:none;
  font-size:12px;
  line-height:30px;
  padding:0 10px;
  cursor:pointer;
}
.select_container li:hover,.select_container li.cur{
  background:#dbf0ff;
}

 

前期工作做好之後,可以開始編寫javascript程式碼了。按照慣例,還是把功能分析為一個一個步驟,再寫具體的程式碼,思路會很清晰。

1. 獲取已有的下拉框元素

這裡需要獲取多個元素,首先通過id獲取select元素,再找到select元素的父元素。因為生成的美化下拉框需要放到父元素中。還要獲取到select所有的option子節點。如下所示:

//獲取下拉框
var eSelect = document.getElementById('selectElem');
//獲取下拉框父節點
var eDd = eSelect.parentNode;
//獲取下拉框選項
var aOptions = eSelect.getElementsByTagName('option');

 

2. 建立美化下拉框元素

先看一下美化後的下拉框,如下圖所示:

 

 

 想一下這個下拉框應該包含哪些元素,一個div元素把所有內容包含在裡面;一個input文字框,顯示選中的值;input元素還需要一個父級容器div元素;一個ul加一組li元素組成下拉選單。好,知道需要哪些元素了,先來建立文字框部分,如下所示:

//建立美化select容器
var eContainer = document.createElement('div');
eContainer.className = 'select_container';
//建立input父級容器
var eInputCon = document.createElement('div');
eInputCon.className = 'input_container';
//建立input文字框,顯示選中的值
var eInput = document.createElement('input');
//設定文字框不能輸入
eInput.readOnly = true;
eInput.placeholder = '請選擇';
//把文字框放到容器中
eInputCon.appendChild(eInput);

再來建立下拉選單。下拉選單可以建立一個ul元素,通過遍歷aOptions下拉框選項,組合成li列表的字串,通過innerHTML放到ul元素中,實現程式碼如下:

//建立ul元素,作為下拉選單容器
var eUl = document.createElement('ul');
//宣告變數,用於組合下拉選單的字串
var sLi = '';
//遍歷原有下拉框選項
for(let i=0;i<aOptions.length;i++){
  //判斷是否是當前選中的選項
  if(aOptions[i].selected){
    //下拉選項組合到下拉選單字串,當前選中的選項需要加上class新增當前樣式
    sLi += '<li class="cur" data-val="'+aOptions[i].value+'">'+aOptions[i].innerHTML+'</li>';
    //當前選中的選項顯示到文字框中
    eInput.value = aOptions[i].innerHTML;
    //eUl元素設定data-val屬性值為當前選中選項的值
    eUl.dataset.val = aOptions[i].value;
  }else{
    //非當前選中的選項,直接組合到下拉選單字串,值設定到data-val屬性中
    sLi += '<li data-val="'+aOptions[i].value+'">'+aOptions[i].innerHTML+'</li>';
  }
}
//下拉選單放到eUl元素中
eUl.innerHTML = sLi;
//預設隱藏下拉選單
eUl.style.display = 'none';

把建立好的這些元素巢狀好,再新增到原下拉框的父元素中,並隱藏原有的下拉框,如下所示:

//把文字框放到eContainer容器中
eContainer.appendChild(eInputCon);
//把下拉選單放到eContainer容器中
eContainer.appendChild(eUl);
//把eContainer元素放到select元素後面
eDd.appendChild(eContainer);
//隱藏原下拉框元素
eSelect.style.display = 'none';

現在效果是有了,但下拉框的功能還沒實現,還需要給這些美化後的元素新增事件。

 

3. 實現開啟下拉框功能

下拉框原有的功能是在文字框上點選滑鼠,就會顯示下拉選單。所以在eInput元素上繫結click事件來顯示下拉選單,如下所示:

//設定下拉框開啟狀態,0為關閉,1為開啟
var status = 0;
//繫結click事件,用於顯示下拉選單
eInput.addEventListener('click',event=>{
  //判斷下拉框是否已開啟
  if(status){
    //下拉框開啟則關閉下拉框
    eUl.style.display = 'none';
  }else{
    //下拉框關閉則開啟下拉框
    eUl.style.display = 'block';
  }
  //修改下拉框狀態
  status = +!status;
},false);

可以看到,在eInput元素上點選,可以開啟和關閉下拉框了。但選擇下拉選項還是無效的。在下拉選項上也繫結click事件

 

4. 下拉選項繫結click事件,點選時修改下拉框的值,如下所示:

//獲取下拉選項列表元素的集合
var eLi = eUl.getElementsByTagName('li');
//遍歷下拉選單
for(let i=0;i<eLi.length;i++){
  //給每一個li元素繫結點選事件
  eLi[i].addEventListener('click',(event)=>{
    //修改下拉框狀態為已關閉狀態
    status = 0;
    //清除所有列表的class
    for(let n=0;n<eLi.length;n++){
      eLi[n].className = '';
    }
    //啟用當前列表選中樣式
    eLi[i].className = 'cur';
    //設定下拉框當前選中值
    eUl.dataset.val = eLi[i].dataset.val;
    //還需要把值設定到原select元素上
    eSelect.value = eUl.dataset.val;
    //觸發原select上的函式
    typeof eSelect.onchange=='function'&&select.onchange();
    //修改eInput元素顯示的值
    eInput.value = eLi[i].innerHTML;
    //關閉下拉框
    eUl.style.display = 'none';
  },false);
}

目前為止,功能基本完成。不過只能在選擇選項後或再在文字框上點選才能關閉下拉框。所以還需要修改一下功能,在頁面其他位置點選時也能關閉下拉框

 

5. 在document繫結點選事件,用於關閉下拉框。

注意兩點:一是在下拉框關閉時需要取消document上的繫結事件;二是eContainer元素上點選時需要阻止冒泡,否則下拉框會打不開了。修改後的程式碼如下:

//設定下拉框開啟狀態,0為關閉,1為開啟
var status = 0;
//繫結click事件,用於顯示下拉選單
eInput.addEventListener('click',event=>{
  //判斷下拉框是否已開啟
  if(status){
    //下拉框開啟則關閉下拉框
    eUl.style.display = 'none';
    //取消document上的繫結事件
    document.removeEventListener('click',closeUl);
  }else{
    //下拉框關閉則開啟下拉框
    eUl.style.display = 'block';
    //在document上繫結點選事件,用於關閉下拉框
    document.addEventListener('click',closeUl,false);
  }
  //修改下拉框狀態
  status = +!status;
},false);

//獲取下拉選項列表元素的集合
var eLi = eUl.getElementsByTagName('li');
//遍歷下拉選單
for(let i=0;i<eLi.length;i++){
  //給每一個li元素繫結點選事件
  eLi[i].addEventListener('click',(event)=>{
    //修改下拉框狀態為已關閉狀態
    status = 0;
    //清除所有列表的class
    for(let n=0;n<eLi.length;n++){
      eLi[n].className = '';
    }
    //啟用當前列表選中樣式
    eLi[i].className = 'cur';
    //設定下拉框當前選中值
    eUl.dataset.val = eLi[i].dataset.val;
    //還需要把值設定到原select元素上
    eSelect.value = eUl.dataset.val;
    //觸發原select上的函式
    typeof eSelect.onchange=='function'&&select.onchange();
    //修改eInput元素顯示的值
    eInput.value = eLi[i].innerHTML;
    //關閉下拉框
    eUl.style.display = 'none';
    //取消document上的繫結事件
    document.removeEventListener('click',closeUl);
  },false);
}

//繫結到document上關閉下拉框的函式
function closeUl(){
  //修改下拉框狀態為已關閉狀態
  status = 0;
  //關閉下拉框
  eUl.style.display = 'none';
  //取消document上的繫結事件
  document.removeEventListener('click',closeUl);
}

//阻止冒泡,否則點選時冒泡到document上,會導致下拉框剛開啟就關閉
eContainer.addEventListener('click',event=>{
  event.stopPropagation();
});

一個美化後的下拉框已經完成,如果動手一步一步實現它,理解應該會更深一些。

相關文章