Использовал для своего селекта концепцию, когда элементы с определенными стилями динамически заменяются, таким образом, код страницы не загружен, все сделано в библиотеке js.
Использовал фреймворк jQuery, пример можно посмотреть на картинке - свободное состояние, нажата кнопка и выбрана опция.
На странице селект размещен так:
<select id="our_select" class="redesign_select w300">
При динамической загрузке контента или если без динамики, то просто по окончании загрузки страницы вызывается скрипт:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
var selects = $(container + " .redesign_select");
for (var i = 0 , select; select = selects[i]; i++) {
if ($(select).hasClass("w300")) element_width = 300 ; //могут быть разных ширин
select.style.display = "none"; //прячем оригинальный селект
var select_area = $("<div>") //область нового селекта
.append($("<div>")
.attr("className", "element_left"))
.attr({"className": select.className + " element_container", "id": "redesign_" + select.id})
.css("width", element_width);
var select_right = $("<div>") //правая часть (где будет кнопка)
.attr("className", "select_right")
.appendTo(select_area)[ 0 ];
var select_center = $("<div>") //декоративная левая часть
.attr({id: "center_" + select.id, className: "element_center"})
.css("width", element_width - 40 )
.appendTo(select_area)[ 0 ];
var select_button = $("<a></a>") //кнопка выбора
.attr("className", "select_button")
//нажимаем - вызывается функция, где прячется или показывается область опций
.bind("click", {select_id: select.id}, function(event) {showSelectOptions(event.data.select_id)})
//если нажата кнопка и мышка ушла не на область опций - закрываем
.bind("mouseout", {select_id: select.id},
function(event){
if (event.relatedTarget.className != "select_options_area" && $("#options_area_" + event.data.select_id).attr("status") != "closed")
$("#options_area_" + event.data.select_id)
.attr("status", "")
.animate({"height": 0 }, $("#options_area_" + event.data.select_id).height() * 2 . 5 , function() {$(this).hide().attr("status", "closed")});
})
.appendTo(select_right);
$("<input type='text'>") //текстовое поле с выбранным полем
.attr({readOnly: "true", className: "select_text", "id": "redesign_" + select.id + "_text"})
.appendTo(select_center);
select.parentNode.insertBefore(select_area[ 0 ], select); //вставляем наш элемент в документ перед оригинальным
var select_options_area = $("<div>") //область опций
.attr({"className": "select_options_area", "id": "options_area_" + select.id})
.css("width", $(select_area).width() - 8 )
//если мышка покидает область опций, то она должна закрыться
.bind("mouseleave", function(event) {
if ($(this).attr("status") == "opened") $(this).attr("status", "")
.animate({"height": 0 }, $(this).height() * 2 . 5 , function() {$(this).hide().attr("status", "closed")});
})[ 0 ];
var select_options = $("<div>") //непосредственно область ссылок (нужна из-за декоративного оформления)
.attr({"id": "options_" + select.id, "className": "options_area", "def_width": $(select_area).width() - 20 })
.css({"width": $(select_area).width() - 20 , "background": "url(images/elements/round_yellow_l.gif) repeat-y 0px 0px #fff",
"border-right": "solid 1px #aaa", "overflow-y": "auto", "overflow-x": "hidden"})
.appendTo(select_options_area);
//декоративное обрамление выпадающего списка - "под стиль"
var bord = $("<div>").width("100%").height( 9 ).appendTo(select_options_area);
$("<div>").width( 7 ).height( 9 ).css({"background": "url(images/elements/round_yellow_lb.gif) no-repeat 0px 0px", "float": "left"}).appendTo(bord);
$("<div>").width($(select_area).width() - 21 ).height( 9 ).css({"background": "url(images/elements/round_yellow_b.gif) repeat 0px 0px", "float": "left"}).appendTo(bord);
$("<div>").width( 6 ).height( 9 ).css({"background": "url(images/elements/round_yellow_rb.gif) no-repeat 0px 0px", "float": "right"}).appendTo(bord);
for (var j = 0 , option; option = select.options[j]; j++) { //цикл заполнения опций
//опция с value="null" отображается в поле селекта по умолчанию, но не показывается среди селектов
if (option.value != "null") $("<a></a>")
.width($(select_area).width() - 30 )
.append(document.createTextNode(option.text))
.bind("click", {select_id: select.id, co: j}, function(event){
showSelectOptions(event.data.select_id);
selectOption(event.data.select_id, event.data.co);
})
.appendTo($("<p>").appendTo(select_options));
if (option.selected) selectOption(select.id, j); //для соответствия оригинальному селекту
}
$("body").append(select_options_area); //вставляем в документ
$(select_options_area) //задаем начальные установки для нового селекта
.attr({"def_height": $(select_options_area).height() + 1 , "status": "closed"})
.height( 0 ).css("display", "none");
}
function showSelectOptions(select_id) {
//задаем установки для выпадающего поля
$("#options_area_" + select_id).css({"left": $("#center_" + select_id).offset().left - 4 , "top": $("#center_" + select_id).offset().top + 20 });
//проверка статуса - открыть или закрыть
if ($("#options_area_" + select_id).attr("status") == "closed") {
$("#options_area_" + select_id).attr("status", "");
$("#options_area_" + select_id)
.show()
.animate({"height": $("#options_area_" + select_id).attr("def_height")}, $("#options_area_" + select_id).height() * 2 . 5 , function() {$(this).attr("status", "opened")})
} else if ($("#options_area_" + select_id).attr("status") == "opened") {
$("#options_area_" + select_id).attr("status", "");
$("#options_area_" + select_id)
.animate({"height": 0 }, $("#options_area_" + select_id).height() * 2 . 5 , function() {$(this).hide().attr("status", "closed")})};
}
//функция для внесения изменений в оригинальный селект, чтобы затем брать значение из него
function selectOption(select_id, option_n) {
$("#" + select_id + " option").attr("selected", function(arr) {return (arr == option_n ? "selected" : "");});
//ну и отображение в текстовом поле выбранного значения - то, что отображается
$("#center_" + select_id + " :text").val($("#" + select_id)[ 0 ].options[option_n].text);
}
//функция для динамического заполнения селекта
function addOptions(select_id, values) {
var link_width = $("#options_" + select_id).attr("def_width") - 10 ;
//если более 7 опций, то добавится скроллер
if (values.length > 7 ) {
$("#options_" + select_id).height( 152 );
$("#options_area_" + select_id).attr("def_height", 163 );
link_width -= 16 ;
} else $("#options_area_" + select_id).attr("def_height", (values.length + 1 ) * 19 + 14 );
//заполнение опций
for (var i in values) {
$("#" + select_id)[ 0 ].options.add(new Option(values[i].text, values[i].value));
$("<a></a>")
.width(link_width)
.append(document.createTextNode(values[i].text))
.bind("click", {sel_id: select_id, co: i - (- 1 )}, function(event) {
showSelectOptions(event.data.sel_id);
selectOption(event.data.sel_id, event.data.co)
})
.appendTo($("<p>").appendTo($("#options_" + select_id)));
};
}
Используемые стили:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
.element_left {
width: 10px;
height: 22px;
float: left;
background: url("../images/elements/element_left.gif") no-repeat 0px 0px;
}
.element_center {
height: 22px;
float: left;
background: url("../images/elements/element_center.gif") repeat 0px 0px;
overflow: hidden;
}
.element_container {
height: 22px;
border: 0px;
float: left;
}
.select_right {
width: 30px;
height: 22px;
float: right;
}
.select_button {
display: block;
width: 30px;
height: 22px;
border: 0px;
cursor: pointer;
text-decoration: none;
background: url("../images/elements/select_right.gif") no-repeat 0px -22px;
outline: none;
}
.select_button:hover {
background-position: 0px 0px;
}
.select_text {
margin-left: 1px;
height: 16px;
width: 100 %;
border: 0px;
font-family: Verdana;
font-size: 8pt;
color: # 666 ;
margin-top: 3px;
* margin-top: 2px;
}
.options_area {
padding: 2px 2px 0px 9px;
font-family: Verdana;
font-size: 8pt;
}
.select_options_area {
position: absolute;
margin-top: -1px;
margin-left: 5px;
z-index: 199 ;
overflow: hidden;
display: block;
border-top: solid 1px #aaa;
}
.options_area p {
margin: 0px;
padding: 0px;
cursor: pointer;
line-height: 15px;
}
.options_area a {
color: # 666 ;
text-decoration: none;
display: block;
padding: 1px 4px;
outline: none;
border: solid 1px #fff;
}
.options_area a:hover {
background-color: #ffde59;
border: solid 1px #aaa;
}
- выпадение и свертывание опций анимировано;
- скорость прокрутки не зависит от высоты;
- возможность стилевого оформления;
- почти полная идентичность оригинальному селекту по функциональности;
- использован статус открывания опций - не возникнет коллизий, когда список открывается и нажимают на кнопку закрытия и он с полдороги будет закрываться - откроется и сам закроется;
- внутренние элементы (в частности, текстовое поле) имеют свои id, которые связаны с айди оригинального селекта и к которым можно обратиться.
Ошибки и недоделки, на которые хотелось бы обратить внимание (в большинстве своем не сделаны в силу того, что проект пока не требовал этих доделок):
- не задействована клавиатура: в спокойном состоянии кнопка вниз, в раскрытом - хождение и выбор - планирую сделать в ближайшее время, так как не очень удобно бегать по форме;
- (!!! возможно, поможете, не могу найти решения) если открыть и очень быстро переместить мышку на поле опций, то оно закроется, при последующих открываниях этого не наблюдается;
- по умолчанию список заменяется на нескороллируемый, функционал для заполнения длинных списков задействован отдельно (последняя функция) - не возникало потребности использовать длинные статические списки;
- для обнуления значения элемента (типа form clear) приходится пока использовать 2 строчки - одна обнуляет оригинальный селект, а вторая ставит в текстовое поле нового элемента значение из оригинального - не очень удобно, но не занимался этим вопросом.
Те, кто уже пользуются элементами (как селектами, так и прочими - инпутами, текстовыми блоками), дискомфорта не испытывают и работой элементов остались довольными.