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(); });
一個美化後的下拉框已經完成,如果動手一步一步實現它,理解應該會更深一些。