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;//清除所有列表的classfor(let n=0;n<eLi.length;n++){eLi.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;//清除所有列表的classfor(let n=0;n<eLi.length;n++){eLi.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();});
一个美化后的下拉框已经完成,如果动手一步一步实现它,理解应该会更深一些。