cuSel — нестандартные select на jQuery

Подготовил: Евгений Рыжков Дата публикации: 24.06.2010
Последнее обновление: 28.06.2011

Задача

Изменить дизайн select. Например на такой:
пример нестандартного select

Требования

  • легкость интеграции
  • кроссбраузерность
  • быстрая скорость работы
  • максимальная приближенность к работе обычного select:
    • на сервер передается значение (value выбранного option)
    • по клику на option выпавший список скрывается
    • по клику вне select выпавший список скрывается
    • в выпадающем списке показ полностью длинных значений option
    • учет атрибута disabled
    • учет атрибута selected
    • если явно не указан selected, select принимает значение первого в списке option
    • учет события onchange
    • скроллинг выпадающего списка прокручивается колесом мыши
    • возможность получения фокуса (update 31.07.10)
    • участие в tab обходе (update 31.07.10)
    • смена значения select стрелками вверх/вниз (update 31.07.10) — после выбора нужного значения требуется нажатие enter
    • смена значения select по нажатию клавиш букв (update 31.07.10)
    • подсветка select при наведении и получении фокуса (update 31.07.10)
    • закрытие списка по Esc (update 3.08.10)
  • при отключенном javascript остаются обычные select
  • удобное динамическое обновление оптионов
  • простота динамического добавления select
  • дополнительные приятные возможности:
    • подсветка в выпадающем списке выбранного значения
    • подсветка в выпадающем списке значения при наведении
    • возможность ручного управления видом скроллинга выпадающего списка
    • возможность оформления option с помощью CSS
    • возможность вставки тегов в option
    • плавное обрезание невмещающегося текста

Демо пример возможностей cuSel (или можно забрать архивом). Проверено в:

  • IE 6-8
  • Firefox 4
  • Opera 9.5-11
  • Safari 5
  • Chrome 4

Что качать

Заметка

Для плавного обрезания текста спрайт должен иметь градиент, переходящий от фона селекта в прозрачность и расположенный левее правой кнопки списка:

спрайт для cusel

При таком эффекте для IE6 подключаем DD_belatedPNG.

Если охота посмотреть код

Быстрый старт

Подключаем необходимые стили и библиотеки:

HTML
01<link rel="stylesheet" type="text/css" href="path-to/cusel.css" />
02<script type="text/javascript" src="path-to/jquery-1.6.1.js"></script>
03<script type="text/javascript" src="path-to/cusel-min.js"></script>
04<!-- если требуется поддержка ie6, учим его понимать альфапрозрачность для плавного обрезания текста -->
05<!--[if IE 6]>
06<script type="text/javascript" src="path-to/DD_belatedPNG.js"></script>
07<script type="text/javascript">
08DD_belatedPNG.fix(".cuselFrameRight"); <!-- именно этот элемент имеет полупрозрачность -->
09jQuery(document).ready(function(){ <!--делаем эмуляцию min-width: 100% для выпадающего списка для IE6 (спасибо Константину Ершову) -->
10jQuery(".cusel").each(
11function(){
12    var w = parseInt(jQuery(this).width()),
13        scrollPanel = jQuery(this).find(".cusel-scroll-pane");
14    if(w>=scrollPanel.width())
15    {
16        jQuery(this).find(".jScrollPaneContainer").width(w);
17        scrollPanel.width(w);
18    }
19});
20});
21</script>
22<![endif]-->

Инициализируем cusel, указывая, где и как стилизовать select:

Javascript
1jQuery(document).ready(function(){
2var params = {
3        changedEl: ".lineForm select",
4        visRows: 5,
5        scrollArrows: true
6    }
7    cuSel(params);
8});

А теперь подробней.

Как это работает

При загрузке страницы, если у пользователя включен javascript:

  1. находятся все указанные селекты на странице
  2. заменяется каждый select
    HTML
    1<select id="idSelect" name="nameSelect">
    2<option value="значение option 1">текст option 1</option>
    3...
    4<option value="значение option N">текст option N</option>
    5</select>
    на конструкцию вида:
    HTML
    01<div class="cusel" id="cuselFrame-idSelect">
    02    <div class="cuselFrameRight"></div>
    03    <div class="cuselText">текст option 1</div>
    04    <div class="cusel-scroll-wrap">
    05        <div class="cusel-scroll-pane" id="cusel-scroll-idSelect">
    06        <span class="cuselActive" value="значение option 1">текст option 1</span>
    07        ...
    08        <span value="значение option N">текст option N</span>
    09        </div>
    10    </div>
    11    <input id="idSelect" name="nameSelect" value="значение option 1" type="hidden"/>
    12</div>
  3. обычные select удаляются

Заметка

Обязательным условием является указание id селекту, т.к. на основании этого уникального идентификатора гарантируется работа скроллингов в выпадающих меню.

Инициализация cuSel

Функции

cuSel производит замену обычных select на стилизованные. Меняет только те, которые указаны в параметре changedEl.
cuSelRefresh производит обновление селекта. Необходимо при изменении количества option в select и при показе скрытого select. Обновляет селекты, указанные в параметре refreshEl.

Параметры

changedEl указываем селекторы (за основу взяты базовые селекторы CSS1) , к которым будет применен cuSel. Можно указать элемент (тег), класс, id. Обязательный параметр. Используется только в функции cuSel.
visRows количество видимых строк выпадающего списка. Если в списке элементов будет больше заданного числа, у списка появится вертикальная прокрутка. Если число не задано, все значения списка будут видимы.
scrollArrows отражать или нет стрелочки в блоке с прокруткой. Возможные значения: true/false (по умолчанию false)
refreshEl id селектов через запятую, которые требуется обновить. Параметр обязателен. Используется только в функции cuSelRefresh.

Примеры

Меняем все селекты на странице, без ограничения по высоте для выпадающих списков:

Javascript
1var params = {
2        changedEl: "select"
3    }
4cuSel(params);

Меняем один конкретный селект по id, число видимых элементов выпадающего списка 7:

Javascript
1var params = {
2        changedEl: "#city",
3        visRows: 7
4    }
5    cuSel(params);

Цепляем onchange списку. Сusel-селект имеет структуру, построенную на div и span. Это нужно учитывать при динамическом изменении содержимого списков:

HTML
1<select id="city" name="city" onChange="alert('произошел onchange')">
2<option value="1">Киев</option>
3<option value="2">Львов</option>
4<option value="3">Дон</option>
5<option value="4">Ялта</option>
6</select>
Javascript
1var params = {
2        changedEl: "#city"
3    }
4    cuSel(params);

Если при загрузке страницы select был скрыт, то при его показе ему нужно обязательно указывать width и провести обновление:

Javascript
01jQuery("#showSel").click(
02function()
03{
04    jQuery("#hidden-select")
05        .css("display","block")
06        .css("width","100px"); /* показанному селект указываем width */
07params = {
08    refreshEl: "#hidden-select",
09    visRows: 4
10    }
11    cuSelRefresh(params);
12});

Этого указания ширины можно избежать, если изначально select был присвоен класс, с заданной шириной через !important. Тут же, покажу как обновить сразу два селекта, которые были скрыты:

HTML
01<div id="hidden-block" style="display: none">
02    <select id="city2" name="city2" class="wid100">
03    <option value="1">Киев</option>
04    <option value="2">Львов</option>
05    <option value="3">Донецк</option>
06    <option value="4">Ялта</option>
07    </select>
08    <select id="city3" name="city3" class="wid100">
09    <option value="1">Киев</option>
10    <option value="2">Львов</option>
11    <option value="3">Донецк</option>
12    <option value="4">Ялта</option>
13</select>
CSS
1.wid100 {
2width: 100px !important; /* !important обязателен */
3}
Javascript
1jQuery("#hidden-block").css("display","block");
2    params = {
3    refreshEl: "#city2, #city3", /* перечисляем через запятую id селектов, которые нужно обновить */
4    visRows: 4
5    }
6    cuSelRefresh(params);

Динамическое добавление select:

Javascript
01jQuery("#addSelect").click(
02function()
03{
04    var addedSelect =   '<select id="add-select" name="add-select">'+
05                        '<option value="1">доллар США</option>'+
06                        '<option value="2">доллар канадский</option>'+
07                        '<option value="3">доллар новозеландский</option>'+
08                        '</select>';
09    jQuery(this).replaceWith(addedSelect);
10     
11    var params = {
12        changedEl: "#add-select",
13        visRows: 4
14    }
15    cuSel(params);
16     
17});

Динамическое добавление опционов:

Javascript
01jQuery("#addAnimals").click(
02function()
03{
04    /* при добавлении следует учитывать, что option были заменены на span */
05    var newAnimals = '<span val="4">Слон</span><span val="5">Жираф африканский</span>';
06     
07    /* id контенера, где содержаться "option" получаем соединяя префикс cusel-scroll- и id select */
08    jQuery("#cusel-scroll-animals").append(newAnimals); 
09     
10             
11     
12    var params = {
13        refreshEl: "#animals",
14        visRows: 4
15    }
16    cuSelRefresh(params);
17     
18});

Дополнительные теги внутри option реализуются через дополнительный атрибут addTags. Если он присутствует, его содержимое добавится в соответствующий "новый" option:

HTML
1<select id="animals" name="animals">
2        <option value="1" addTags="<div class='kv'></div>">Медведь</option>
3        <option value="2" addTags="<input type='checkbox' />">Волк</option>
4        <option value="3">Заяц</option>
5        <option value="4">Ежик</option>
6</select>

Часто задаваемые вопросы

Как на одной странице сделать два селекта разного вида

Нужно привязывать оформление селекта относительно своего родителя:

CSS
1.parent1 .cusel {
2/* описываем оформление селекта первого вида */
3}
4.parent2 .cusel {
5/* описываем оформление селекта второго вида */
6}

Можно подключить разные фоны, использовать разные цветовые схемы, шрифты и т.п.

Или же можно использовать дополнительный класс:

HTML
1<select id="select1" name="select1">
2...
3<select id="select2" name="select2" class="cusel2">
4...
CSS
1.cusel {
2/* описываем оформление селекта первого вида */
3}
4.cusel2 {
5/* описываем оформление селекта второго вида */
6}

Как убрать выделение выбранного пункта в выпадающем списке

Из CSS удаляем класс .cuselActive.

Как задать ширину стилизированного select

В CSS указать явно ширину классу .cusel или добавить класс, с явно заданной шириной заменяемому select.

В прозрачной части cuSel виден фон, который находится под ним

Следует делать спрайт более длинным, чтобы непрозрачный фон являлся подложкой селекту, тогда прозрачный участок ляжет сверху на эту подложку и cusel просвечиваться не будет.

Как убрать подсветку select при наведении и получении фокуса (update 31.07.10)

Достаточно убрать из стилей:

CSS
01/*
02    styles for focus and hover
03*/
04.cusel:hover,
05.cusel:hover .cuselFrameRight,
06.cusel:focus,
07.cusel:focus .cuselFrameRight,
08.cuselFocus,
09.cuselFocus .cuselFrameRight {
10    background-image: url(path-to/selects-focus.png);
11}

Как динамически управлять disabled

Пример приведен в перечне доработок версии 2.3.

Улучшения по сравнению с предыдущей версией:

  • работает в разы быстрее. Даже в IE6 при довольно большом количестве селектов на странице, замена происходит достаточно быстро.
  • скрипт стал в 2 раза легче
  • cuSel по поведению более похож на обычный селект
  • более прост в обращении, особенно для программистов, которым теперь не нужно ломать голову как привязать onchange

Предыдущая версия

  • selects.png (370b) — составная картинка для всех частей селекта (удобно использовать в качестве примера)
  • selects.css (3.5 Kb) — таблица стилей для нестандартных select
  • selectsComments.css (6.5 Kb) — таблица стилей с комментариями
  • jquery.js (55.9 Kb) — библиотека jQuery версии 1.3.2
  • jsScroll.js (8.13 Kb) — библиотека для реализации нестандартно оформленного скроллинга
  • jquery.selects.js (12 Kb) — библиотека замены select на нестандартные
  • jquery.selects_pack.js (3 Kb) — сжатый скрипт

Баги/недостатки cuSel

  • У option должен обязательно быть атрибут value, хотя бы пустой — это исправлять пока не вижу смысла, потому что нет понту от селекта, который не передает значения. А для исключений можно дописать value="". устранено
  • список выпадает всегда вниз
  • в IE6 есть нет реакции на :hover
  • если по пунктам выпавшего списка пробовать перемещаться и мышью и клавиатурой, могут проскакивать некоторые глюки
  • нет поддержки <optgroup>, более того, скорей всего селект, содержащий optgroup не будет функционировать.

Пожелания / предложения

  • by f0rmateg: раскрытие списка по нажатию на пробел, по аналогии как реализовано в Opera для обычных select
  • by Олег Панов: предусмотреть реализацию для списков вида <select multiple size="1">
  • by Николай: предусмотреть возможность динамически дизэйблить и энэйблить селекты. реализовано

Апдейты: что было исправлено/добавлено

Показать комментарии