自定义样式的select下拉框深入探索

时间:2023-03-08 16:12:26
自定义样式的select下拉框深入探索

第一个版本:

首先实现自定义select下拉框应该具有的功能,我是选择将原来的select隐藏掉,自己在jquery代码中动态写进去<dl><dd><dt>这样的结构来模拟真正的select的操作。

用来模拟select框的div结构如下:

<div class="selectbox">//包裹整个模拟框的盒子

<div class="currentselected"></div>//用于当前默认显示的selected的元素

<div class="selectoption"></div>//模拟弹出的select下拉框<dl><dd><dt>结构包裹在其中

</div>

代码如下:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title></title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="jquery.min.js"></script> <style>
p{float:left;margin:20px;font-size:14px} .i_selectbox {
margin-top:20px;
height:32px;
position:relative;
font-size:14px
}
/* 默认显示的select框*/
.i_selectbox .i_currentselected {
width:150px;
height:24px;
background:url(select.png) 176px 4px no-repeat;
border:1px solid #cdcdcd;
text-indent:10px;
line-height:24px;
cursor:pointer
}
/* 下拉选择框 */
.i_selectbox .i_selectoption {
overflow-x:hidden;
overflow-y:hidden;
position:absolute;
left:0px;
top:17px;
padding:5px;
background-color:#fff;
background:rgba(255,255,255,.9);
border:1px solid #eee
}
.i_selectbox .i_selectoption dt {
height:24px;
background-color:#eee;
text-indent:5px;
font-style:italic;
color:#555;
line-height:24px;
}
.i_selectbox .i_selectoption dd{
margin-left: -3px;
height:30px;
border-bottom:1px solid #cdcdcd;
cursor:pointer;
text-indent:2px;
line-height:30px
}
.i_selectbox .i_selectoption dd:hover{
background-color:#cdcdcd;
color:#fff
}
.i_selectbox .i_selectoption dd.selected{
background-color:#f17f36;
color:#fff
}
</style>
<head>
<body>
<select class="my_select"> <option value="0">Alabama</option>
<option value="1">Alaska</option>
<option value="2">Arizona</option> <option value="3">Arkansas</option>
<option value="4">California</option>
<option value="5">Colorado</option> <option value="6">Connecticut</option>
<option value="7">Delaware</option>
<option value="8">Columbia</option>
<option value="9">Florida</option> </select>
<script> /*插件写法的函数*/
$.fn.newStyle = function(){
var set = {
selectbox : 'i_selectbox',
showselect : 'i_currentselected',
selectoption : 'i_selectoption',
curselect : 'selected',
width : 200,
height :150,
zindex : 2
};
/*让最初的select隐藏*/
$(this).hide();
/*动态写进去html替代原本的select*/
var html = '<div class="'+set.selectbox+'" style="zindex:'+set.zindex+'">'
+'<div class="'+set.showselect+'" style="width:'+set.width+'px;">'+$(this).find('option:selected').text()+'</div>'
+'<dl class="'+set.selectoption+'" style="display:none;width:'+set.width+'px" >';
if($(this).find('optgroup').size()>0){
$(this).find('optgroup').each(function(){
html += '<dt>'+$(this).attr('label')+'</dt>';
$(this).find('option').each(function(){
if($(this).is(':selected')){
html += '<dd class="'+set.curselect+'">'+$(this).text()+'</dd>';
}else{
html += '<dd>'+$(this).text()+'</dd>';
}
});
});
}else{
$(this).find('option').each(function(){
if($(this).is(':selected')){
html += '<dd class="'+set.curselect+'">'+$(this).text()+'</dd>';
}else{
html += '<dd>'+$(this).text()+'</dd>';
}
}); }
/*将html插入到select框的后面*/
$('select').after(html);
console.log(0);
/*添加事件*/ /*默认显示框的选择事件toggle选择*/
$('.'+set.showselect).toggle(function(){
console.log(1);
/*$('.selectoption').hide();*/
$('.'+set.selectbox).css('zindex',set.zindex);
$('.'+set.selectoption).css('zindex',set.zindex+1);
$('.'+set.selectoption).toggle();
},function(){
$('.'+set.selectoption).css('zindex',set.zindex);
$('.'+set.selectoption).toggle();
});
/*下拉列表的选择事件*/
$('.'+set.selectoption).find('dd').click(function(){
console.log(2);
$(this).addClass(set.curselect).siblings().removeClass(set.curselect);
var index = $('.'+set.selectoption).find('dd').index(this);
$('.'+set.showselect).text($(this).text());
$(this).find('option').eq(index).attr('selected','selected');
$('.'+set.selectoption).hide();
});
/*点击select框之外的其他部分的时候select框应该收起*/
$('html,body').click(function(e){
/* 判断当前的点击事件目标不是set.selectoption这个class*/
if(e.target.className.indexOf(set.selectoption)==-1){
$('.'+set.selectoption).hide();
$('.'+set.selectbox).css('zIndex',set.zindex);
}
}); } $('.my_select').newStyle(); </script>
</body>
</html>

TIPs:1.给默认显示的下拉框添加toggle事件而不是一般的click事件使得它的连续点击具有意义;

2.给html,body添加事件,判断在点击自定义的下拉框之外的任何地方的时候,自定义下拉框收起。

本来我也以为,一段代码一个功能这样写完就可以了,但是继续改进下去,可以学到的东西真的很多。

第二个版本:

这里是给我自定义的select下拉框添加了键盘选择事件的版本,上下键选择,同时实时更新默认显示框中的内容,esc键实现自定义下拉框收起,此时上下键依然可以实时选择。

代码如下:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title></title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="jquery.min.js"></script> <style> /* 这里来定义你自己的select框的样式 */
.i_selectbox {
margin-top:20px;
height:32px;
position:relative;
font-size:14px
}
/* 默认显示的select框*/
.i_selectbox .i_currentselected {
width:150px;
height:24px;
background:url(select.png) 176px 4px no-repeat;
border:1px solid #cdcdcd;
text-indent:10px;
line-height:24px;
cursor:pointer
}
/* 这里是隐藏的input框 */
#hiddenput {
display: block;
width:200px;
height:18px;
margin-top: -25px;
position: absolute;
z-index:-2;
opacity:0;
filter:alpha(opacity=0);
}
/* 下拉选择框 */
.i_selectbox .i_selectoption {
overflow-x:hidden;
overflow-y:hidden;
position:absolute;
left:0px;
top:17px;
padding:5px;
background-color:#fff;
background:rgba(255,255,255,.9);
border:1px solid #eee
}
.i_selectbox .i_selectoption dt {
height:24px;
background-color:#eee;
text-indent:5px;
font-style:italic;
color:#555;
line-height:24px;
}
.i_selectbox .i_selectoption dd{
margin-left: -3px;
height:30px;
border-bottom:1px solid #cdcdcd;
cursor:pointer;
text-indent:2px;
line-height:30px
}
.i_selectbox .i_selectoption dd:hover{
background-color:#cdcdcd;
color:#fff
}
.i_selectbox .i_selectoption dd.selected{
background-color:#f17f36;
color:#fff
}
</style>
<head>
<body>
<select class="my_select"> <option value="0">Alabama</option>
<option value="1">Alaska</option>
<option value="2">Arizona</option> <option value="3">Arkansas</option>
<option value="4">California</option>
<option value="5">Colorado</option> <option value="6">Connecticut</option>
<option value="7">Delaware</option>
<option value="8">Columbia</option>
<option value="9">Florida</option> </select>
<script>
(function($){
/*插件写法的函数*/
$.fn.newStyle = function(){
var set = {
selectbox : 'i_selectbox',
showselect : 'i_currentselected',
selectoption : 'i_selectoption',
curselect : 'selected',
width : 200,
height :150,
zindex : 2
};
/*让最初的select隐藏*/
$(this).hide();
/*动态写进去html替代原本的select*/
var html = '<div class="'+set.selectbox+'" style="zindex:'+set.zindex+'">'
+'<div class="'+set.showselect+'" style="width:'+set.width+'px;">'+$(this).find('option:selected').text()+'</div>'
+'<input type="text" id="hiddenput" name="hiddenput"/>'
+'<dl class="'+set.selectoption+'" style="display:none;width:'+set.width+'px" >';
if($(this).find('optgroup').size()>0){
$(this).find('optgroup').each(function(){
html += '<dt>'+$(this).attr('label')+'</dt>';
$(this).find('option').each(function(){
if($(this).is(':selected')){
html += '<dd class="'+set.curselect+'">'+$(this).text()+'</dd>';
}else{
html += '<dd>'+$(this).text()+'</dd>';
}
});
});
}else{
$(this).find('option').each(function(){
if($(this).is(':selected')){
html += '<dd class="'+set.curselect+'">'+$(this).text()+'</dd>';
}else{
html += '<dd>'+$(this).text()+'</dd>';
}
}); }
/*将html插入到select框的后面*/
$('select').after(html); /*为了写起来方便,给外围的box和当前显示的select框还有自定义的下拉框一个简单的变量*/
thisbox = $('.'+set.selectbox);
thisselect = $('.'+set.showselect);
thisoption = $('.'+set.selectoption);
/*添加事件*/ /*默认显示框的选择事件,点击默认显示框,下拉列表会在可见与不可见状态之间切换*/
thisselect.toggle(function(){
/*$('.selectoption').hide();*/
thisbox.css('zindex',set.zindex);
thisoption.css('zindex',set.zindex+1);
thisoption.toggle();
},function(){
thisoption.css('zindex',set.zindex);
thisoption.toggle();
}); /*下拉列表的选择事件*/
thisoption.find('dd').click(function(){
$(this).addClass(set.curselect).siblings().removeClass(set.curselect);
var index = thisoption.find('dd').index(this);
thisselect.text($(this).text());
$(this).find('option').eq(index).prop('selected',true);
thisoption.hide();
}); /*点击默认显示框时要给我隐藏的input获得焦点*/
thisselect.click(function(){
$('input')[0].focus();
}) var index = 0;
/*下拉列表的键盘事件*/
$("input").keyup(function(e){
var keycode = e.which;
console.log(keycode);
var ddtarget = thisoption.find('dd');
/*向下*/
if(keycode == 40){
index++;
console.log(index);
if(index == ddtarget.length){
index = 0;
console.log(index);
}
/* 设为当前选中项,同時在默认显示的select框中同步显示*/
thisoption.find('dd').eq(index).addClass(set.curselect).siblings().removeClass(set.curselect);
var ddindex = thisoption.find('dd').eq(index).index(this);
thisselect.text(thisoption.find('dd').eq(index).text());
$(this).find('option').eq(ddindex).prop('selected',true);
}
/*向上*/
if(keycode == 38){ console.log(index);
if(index == 0){
index = ddtarget.length;
console.log(index);
}
index--;
/* 设为当前选中项,同時在默认显示的select框中同步显示*/
thisoption.find('dd').eq(index).addClass(set.curselect).siblings().removeClass(set.curselect);
var ddindex = thisoption.find('dd').eq(index).index(this);
thisselect.text(thisoption.find('dd').eq(index).text());
$(this).find('option').eq(ddindex).prop('selected',true);
}
/*按下Esc 隐藏弹出层*/
  if (keycode == 27) {
console.log('esc');
if (thisoption.is(":visible")) {
thisoption.hide();
}
}
}); /*点击select框之外的其他部分的时候select框应该收起*/
$('html,body').click(function(e){
/* 判断当前的点击事件目标不是set.selectoption这个class*/
if(e.target.className.indexOf(set.selectoption)==-1){
thisoption.hide();
thisbox.css('zIndex',set.zindex);
}
}); /*取消下拉列表选择事件的冒泡*/
thisoption.find('dd').click(function(e){
e.stopPropagation();
}); }
})(jQuery);
$('.my_select').newStyle(); </script>
</body>
</html>

实现的思路是:将一个input框隐藏在默认显示的selected框下面,点击默认显示框时同时是它获得焦点,然后获得键盘事件,对自定义的select下拉框实现一系列的操作。

关键代码:

 /*点击默认显示框时要给我隐藏的input获得焦点*/
thisselect.click(function(){
$('input')[0].focus();
});

TIPS:1.要有输入,才有键盘的事件,不然怎么读取呢?

2.键盘上下选择时最后一个和第一个的切换值得注意,要连贯。

是的,到这里还不是结束,那么我还可以做点什么呢? 让我的代码更好看一点吧。

第三个版本:

学习插件写法,让我的代码也可以暴露方法给别人用

代码如下:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title></title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="jquery.min.js"></script> <style> /* 这里来定义你自己的select框的样式 */
.i_selectbox {
margin-top:20px;
height:32px;
position:relative;
font-size:14px
}
/* 默认显示的select框*/
.i_selectbox .i_currentselected {
width:150px;
height:24px;
background:url(select.png) 176px 4px no-repeat;
border:1px solid #cdcdcd;
text-indent:10px;
line-height:24px;
cursor:pointer
}
/* 这里是隐藏的input框 */
#hiddenput {
display: block;
width:200px;
height:18px;
margin-top: -25px;
position: absolute;
z-index:-2;
opacity:0;
filter:alpha(opacity=0);
}
/* 下拉选择框 */
.i_selectbox .i_selectoption {
overflow-x:hidden;
overflow-y:hidden;
position:absolute;
left:0px;
top:17px;
padding:5px;
background-color:#fff;
background:rgba(255,255,255,.9);
border:1px solid #eee
}
.i_selectbox .i_selectoption dt {
height:24px;
background-color:#eee;
text-indent:5px;
font-style:italic;
color:#555;
line-height:24px;
}
.i_selectbox .i_selectoption dd{
margin-left: -3px;
height:30px;
border-bottom:1px solid #cdcdcd;
cursor:pointer;
text-indent:2px;
line-height:30px
}
.i_selectbox .i_selectoption dd:hover{
background-color:#cdcdcd;
color:#fff
}
.i_selectbox .i_selectoption dd.selected{
background-color:#f17f36;
color:#fff
}
/* 这里是新定义的按钮 */
#optionShow {
display:block;
position: absolute;
margin-left: 300px; }
#optionHide {
display:block;
position: absolute;
margin-left: 300px;
margin-top: 50px;
} </style> <head>
<body>
<select class="my_select"> <option value="0">Alabama</option>
<option value="1">Alaska</option>
<option value="2">Arizona</option> <option value="3">Arkansas</option>
<option value="4">California</option>
<option value="5">Colorado</option> <option value="6">Connecticut</option>
<option value="7">Delaware</option>
<option value="8">Columbia</option>
<option value="9">Florida</option> </select>
<input type="button" id="optionShow" name="optionShow" value="点击这里显示下拉框"/>
<input type="button" id="optionHide" name="optionHide" value="点击这里隐藏下拉框"/>
<script>
(function($){
/*这个对象定义的位置值得注意*/
var set = {
selectbox : 'i_selectbox',
showselect : 'i_currentselected',
selectoption : 'i_selectoption',
curselect : 'selected',
width : 200,
height :150,
zindex : 2
}
$.fn.extend({ /*插件写法的函数*/
newStyle : function(){ /*让最初的select隐藏*/
$(this).hide();
/*动态写进去html替代原本的select*/
var html = '<div class="'+set.selectbox+'" style="zindex:'+set.zindex+'">'
+'<div class="'+set.showselect+'" style="width:'+set.width+'px;">'+$(this).find('option:selected').text()+'</div>'
+'<input type="text" id="hiddenput" name="hiddenput"/>'
+'<dl class="'+set.selectoption+'" style="display:none;width:'+set.width+'px" >';
if($(this).find('optgroup').size()>0){
$(this).find('optgroup').each(function(){
html += '<dt>'+$(this).attr('label')+'</dt>';
$(this).find('option').each(function(){
if($(this).is(':selected')){
html += '<dd class="'+set.curselect+'">'+$(this).text()+'</dd>';
}else{
html += '<dd>'+$(this).text()+'</dd>';
}
});
});
}else{
$(this).find('option').each(function(){
if($(this).is(':selected')){
html += '<dd class="'+set.curselect+'">'+$(this).text()+'</dd>';
}else{
html += '<dd>'+$(this).text()+'</dd>';
}
}); }
/*将html插入到select框的后面*/
$('select').after(html); /*为了写起来方便,给外围的box和当前显示的select框还有自定义的下拉框一个简单的变量*/
thisbox = $('.'+set.selectbox);
thisselect = $('.'+set.showselect);
thisoption = $('.'+set.selectoption);
/*添加事件*/ /*默认显示框的选择事件,点击默认显示框,下拉列表会在可见与不可见状态之间切换*/
thisselect.toggle(function(){
/*$('.selectoption').hide();*/
thisbox.css('zindex',set.zindex);
thisoption.css('zindex',set.zindex+1);
thisoption.toggle();
},function(){
thisoption.css('zindex',set.zindex);
thisoption.toggle();
}); /*下拉列表的选择事件*/
thisoption.find('dd').click(function(){
$(this).addClass(set.curselect).siblings().removeClass(set.curselect);
var index = thisoption.find('dd').index(this);
thisselect.text($(this).text());
$(this).find('option').eq(index).prop('selected',true);
thisoption.hide();
}); /*点击默认显示框时要给我隐藏的input获得焦点*/
thisselect.click(function(){
/*这里面[0]起了很大作用喔*/
$('input')[0].focus();
}) var index = 0;
/*下拉列表的键盘事件*/
$("#hiddenput").keyup(function(e){
var keycode = e.which;
console.log(keycode);
var ddtarget = thisoption.find('dd');
/*向下*/
if(keycode == 40){
index++;
if(index == ddtarget.length){
index = 0;
}
/* 设为当前选中项,同時在默认显示的select框中同步显示*/
thisoption.find('dd').eq(index).addClass(set.curselect).siblings().removeClass(set.curselect);
var ddindex = thisoption.find('dd').eq(index).index(this);
thisselect.text(thisoption.find('dd').eq(index).text());
$(this).find('option').eq(ddindex).prop('selected',true);
}
/*向上*/
if(keycode == 38){
if(index == 0){
index = ddtarget.length;
}
index--;
/* 设为当前选中项,同時在默认显示的select框中同步显示*/
thisoption.find('dd').eq(index).addClass(set.curselect).siblings().removeClass(set.curselect);
var ddindex = thisoption.find('dd').eq(index).index(this);
thisselect.text(thisoption.find('dd').eq(index).text());
$(this).find('option').eq(ddindex).prop('selected',true);
}
/*按下Esc 隐藏弹出层*/
  if (keycode == 27) {
console.log('esc');
if (thisoption.is(":visible")) {
thisoption.hide();
}
}
}); /*点击select框之外的其他部分的时候select框应该收起*/
$('html').click(function(e){
/* 判断当前的点击事件目标不是set.selectoption这个class*/
if(e.target.className.indexOf(set.selectoption)==-1){
thisoption.hide();
thisbox.css('zIndex',set.zindex);
}
}); /*取消下拉列表选择事件的冒泡*/
thisoption.find('dd').click(function(e){
e.stopPropagation();
}); },/*newStyle函数结束*/
showSelect : function(){
$(this).click(function(e){
$(".i_selectoption").show();
//$('.'+set.selectoption).show();
/*之前曾给html添加过点击selectoption收起的事件啊!!不阻止冒泡的话会发生冲突啊!!问题找了好久啊!!!*/
e.stopPropagation();
}); },
hideSelect : function(){
$(this).click(function(){
$('.'+set.selectoption).hide();
});
}
})
})(jQuery); $('.my_select').newStyle();
$('#optionShow').showSelect();
$('#optionHide').hideSelect(); </script>
</body>
</html>

TIPS:1.如果你想定义一个对象供所有的插件方法可用的话,那么应该定义到上面代码中那样全局的位置;

2.(function($){........})(jQuery);这样写法可以使得外部无法访问到函数内部的公共变量和对象的命名空间;

3.注意可能冲突的事件之间有可能存在事件冒泡的影响!!

4.可以多学习多参考各种各样的代码调试方法,这个真的很重要。

通过一个简单的功能,我学到的东西很多喔,事实证明,冷静的思考,不放弃的探索才是最好的学习办法。