Selectik — стильные селекты

Веб-дизайнеры любят стилизировать стандартные элементы форм. Потом эти элементы приходиться реализовывать нам — верстальщикам.

После десятков проектов, где приходилось использовать сторонние решения, я столкнулся с постоянными изобретениями костылей, не хватки гибкости готовых плагинов, скоростью генерации.

Естественно, после всего этого я решил изобрести свой велосипед.

Пример разработан без дополнительных картинок с помощью CSS3. Демо-страница с песочницей здесь.

Опять велосипед?


Достаточно долго я дописывал, создавал костыли при использовании готовых плагинов. Последней каплей стал тот факт, что тестеры при написании Unit-тестов пытаются достучаться, в первую очередь, до стандартного селекта, но это не выходит, так как большинство плагинов, которые я использовал скрывают стандартный селект через «display=none».

Также я хотел иметь легкое решение, быструю генерацию (в IE при большом количестве селектов на странице), работающие точно также как и оригинальный селект.

Плагины, которые я использовал чаще всего отталкивались от событий связанных с уже созданной структурой стилизированного селекта. В случае Selectik'a, события в основном повешены именно на оригинальный селект.

Библиотекой было решено использовать jQuery, по двум причинам:
  • Сейчас большинство создаваемых сайтов используют именно эту библиотеку.
  • На нативном JavaScript'е создание заняло бы намного больше времени и количество кода зашкаливало.

И так, что же из себя представляет Selectik?


В minify-версии вес файла скрипта всего 5,2 Кб. А с дополнительным плагином (скролл колеса мыши) и CSS в демо-примере 12,8 Кб. GZIP 2,1 Кб/4,7 Кб соответственно.

Поддерживаемые браузеры: IE7+, последние версии Chrome, Safari, Firefox.

Возможности, которые на данный момент реализованы:
  • управление клавишами вверх/вниз и Enter
  • стилизированная прокрутка
  • скролл колесом мыши
  • поиск элементов по буквам
  • автоматически подстраивает ширину
  • «умное позиционирование»
  • реагирует на переход Tab'ом
Параметры, которые можно использовать:
  • назначение ширины списка (в стандартном случае высчитывается ширина оригинального списка)
  • максимальное количество видимых элементов
  • выбора типа прокрутки — стилизированный или стандартный
  • скорость анимации открытия и закрытия списка
API:
  • обновление стилизированного списка
  • открытие/закрытие списка
  • назначение нового активного элемента
  • динамическое изменение ширины
  • включение/выключение списка

В будущем

  • поддержка мобильных устройств
  • ваши пожелания в комментариях
Проект на GitHub. Демо-страница с песочницей здесь.
Буду благодарен, если ошибки/проблемы вы сможете описать в issue.

update
Как зметил Connor требуется версия jQuery 1.7+ из-за использования .on(). Вероятнее всего будет заменено на .bind().

update 2
Всем спасибо за замечания и баги. В течении недели будет сделано.
Поделиться публикацией
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 74
    +3
    А я бы еще порекомендовал эту библиотеку harvesthq.github.com/chosen/, у нее уже довольно большое комьюнити и выглядит покрасивее, хотя это уже на вкус.
      0
      Вы хотели сказать «плагин»? Спасибо, видел его. В моем случае, как я писал, хотелось легкого (вес определяющий), простого и не нагруженого решения.
        0
        у chosen есть один недостаток — подогнать его под основной дизайн сайта довольно сложно. При этом схожих стилей для остальных элементов форм, выполненных в том же стиле как и селекты в нем нет.
          0
          Что-то я не помню сложностей при изменении дизайна. Подогнал идеально под дизайн своего сайта, времени, помнится, заняло совсем немного. Ссылку не кидаю, сочтут за рекламу :)
          0
          Тоже им пользуюсь.
            0
            За этот плагин спасибо!
            0
            Стрелки вверх-вниз вызывают смену выбранного и одновременно с этим открытие-закрытие окна на каждое нажатие стрелкой. Выглядит жутко.
            Opera12/lin/64
              0
              Да, действительно, есть проблема в Опере. Спасибо, будет сделано.
                +2
                В Опере теперь работает. Нашлась интересная загвоздка в поведении Оперы: при нажатии клавиши вверх/вниз на оригинальном селекте отрабатывается событие «клик» мыши.
                  +4
                  Опера полна подобных «загвоздок».
                    0
                    Я старался сделать под все браузеры, но 99% проектов с которыми я работаю не требуют поддержки Оперы.
                      +2
                      Как много интересного вы пропустили в работе…
                        0
                        Возможно. А может у меня появилось больше времени на работу с Javascript, а не разработки костылей.
                0
                могу еще порекомендовать данный вариант linkselect, имеет хороший API
                  0
                  Интересно, но весьма своеобразное решение: оригинальный селект они заменяют на инпут. В моих целях не подходит.
                    0
                    «заменяют на инпут» — в плане внутреннего устройства или внешнего отображения?
                    там есть у них разные варианты дизайна, включая вид обычного селекта
                      +1
                      В плане внутреннего устройства.
                  0
                  Вот тоже, кстати, симпатичный.
                    +1
                    Симпатичный. Подскажите, где вы видите его использование, в каком случае?
                      0
                      Например, я бы поставил такой на apple.com, он бы там смотрелся отлично :)
                        +2
                        В визуальном смысле — да. Практичности не вижу… Поведение селекта достаточно сильно отличается от стандартного. Большинство пользователей могут не понять.
                    0
                    Вы кстати написали бы, что требуется jQuery 1.7+, а то ведь не все веб-мастера обновляют библиотеку(.on() только с 1.7 появился).
                      0
                      Обновил топик.
                      0
                      Если добавить пункт с длинным текстом — начинаются проблемы :)

                      Эту проблему пока ни один заменитель селектов не решает, к сожалению.
                        0
                        Как отписался внизу Rozzy: можно изменить CSS под ваши требования. В стандартной версии решил не скрывать текст — при разработке будет не удобно.
                          0
                          Всё верно, но элемента меняется ширина(«width») с помощью инлайновых стилей.

                          Их можно перебить, но все же.
                            0
                            Возможно вас не понял, но ширину вы можете назначить при вызове плагина.
                        0
                        В таких случаях нужно ограничить ширину (допустим, max-width) и добавить свойство text-overflow.

                        white-space: nowrap;
                        max-width: 200px;
                        text-overflow: ellipsis;
                        -o-text-overflow: ellipsis;
                        overflow: hidden;
                          0
                          Баг двойных стрелок в линуксовом Chrome 17.0.963.26 dev.
                          Эта чёрная стрелка — от дефолтного выпадающего списка, если его спрятать (display: none), то всё нормально.
                            0
                            Такая же версия Win — проблемы нет. На том, что селект виден построен плагин. Так как dev версия — думаю, что баг в Chrome. Но все равно проверю на Ubuntu.
                            +1
                            Не очень удобно, что при повторном нажатии на селект, он не закрывается, как стандартный.

                            Тоже, кстати, писал обзор плагинов и сам остался не очень доволен тогдашним положением дел, всё косо-криво. Скоро попробую ваш вариант, надеюсь, что вы сделали мой идеал =)
                              +1
                              Занес в изменения повторное нажатие. Релизую сегодня-завтра.

                              Как раз на ваш обзор и ориентировался при написании статьи. )
                                0
                                Забрал с github последнюю версию, все-равно при повторно клике заново моргает и появляется список, не сворачивается
                              +1
                              А почему нет поддержки такой удобной вещи как OPTGROUP? На своём веку помню лишь пару плагинов хоть с кривой и упрощенной, но поддержкой групп.
                                0
                                На данный момент занес в TODO возможность отключить стилизицию к селектам с OPTGROUP, и атрибутом multiple select. Повторюсь: идея была только для стандартных селектов без этих возможностей. Редко встречаются. Если надо пддержка такой функциональности — используйте указанный в первом комментарии Chosen.
                                +1
                                Хочется поведения выпадающих списков как у нативных. Кликнули на выпадающий список — он развернулся, кликнули еще раз — у вас он повторно развернулся, хотя в оригинале должен свернуться.
                                  0
                                  Как было указано выше будет сделано.
                                    0
                                    Прошу прощения, сразу не заметил
                                  0
                                  Тестовый пример не работает в IE (причём даже в девятом). Наверное что-то поломалось =)

                                  image
                                    0
                                    Вот ссылка на полноразмерный скрин.
                                      0
                                      В IE8 работает, но не видно галочку справа.
                                        0
                                        Объект селектора :after в CSS не отображаются. Исправлю для IE.
                                    0
                                    Спасибо. Проверю.
                                      0
                                      В мобильном Safari отображается выпадающий список и одновременно с ним появляется стандартный селектор в виде барабана. Выбор пункта на барабане и подтверждение кнопкой Done выделяет пункт в списке, который продолжает «свисать». Приходится повторно тапать по пункту, после чего список наконец сворачивается.
                                        0
                                        Для мобильных устройств, на данный момент, вообще не планировалось использовать — есть стандартное нормальное поведение.
                                          0
                                          Нужно встроить проверку на мобильные браузеры, чтобы селект для них не применялся, т.к. текущее поведение не корректное.
                                            0
                                            Да, добавлю в TODO — спасибо.
                                        0
                                        Ваш плагин я бы не назвал бы велосипедом, действительно предусмотрено большинство событий как у стандартного элемента select. Я бы посоветовал сделать срытие списка не по событию на документ, а при потере фокуса, как это реализовано в виджите popup jQueryUI, чтобы избежать конфликтов с другими плагинами.
                                          0
                                          Тут есть несколько подводных камней, например, при клике по скролу или фону скрола фокус будет теряться. Надо ставить дополнительные проверки. Постараюсь как сделать лучше.
                                            0
                                            В jQuery UI это решается установкой таймера(это я насчет фона), а вот насчет скрола, да здесь стоит подумать.
                                          +1
                                          Ребята, как мне необходима была сегодня эта статья! Спасибо! :)
                                            0
                                            Пользуйтесь на здоровье, только прошу отписать, если будут баги/замечания, чтобы улучшить плагин.
                                            0
                                            Не нашёл в демке — есть ли поддержка кнопки «reset»?
                                            Пока ни у одного самописного селекта не видел сброса в начальное состояние при нажатии reset в форме, каждый раз приходится писать костыли вручную, поскольку спрятанный оригинальный селект сбрасывается, а на подменяющем его скриптовом селекте это никак не отражается.
                                              +1
                                              Нет, спасибо за замечание — сделаю.
                                              0
                                              О ужас, не хотелось бы чтоб это было повсеместно.
                                                0
                                                Что именно?
                                                  0
                                                  такие селекты и прочее, очень в редких случаях это можно встроить в дизайн, чаще это излишнее…
                                                    0
                                                    А можно поинтересоваться, по какой причине их сложно встроить в дизайн?
                                                      0
                                                      Согласен с частью: да, мне как верстальщику легче оригинальные элементы, так привычнее и проще, но дизайн элементов все чаще и чаще изменяется (например элементы форм в наших мобильных устройствах). И не скажу, что это излишне…
                                                  +1
                                                  Не стоит использовать классы вроде «done, open». Очень велика вероятность того, что аналогичные классы встретятся в проекте, где ваш селект будет использоваться (custom-select тоже кстати весьма популярный класс). Сделайте возможность задания своего класса, а вспомогательные классы пусть будут генерироваться с префиксом (.your-class-name-select-open, .your-class-name-select-done).
                                                    +1
                                                    Сделаю, спасибо за замечание.
                                                    +2
                                                    Вообще селект понравился. Аккуратная верстка, симпатичен внешне. Буду контрибьютить.
                                                      +2
                                                      Правда есть вопросы по js:

                                                      1. Зачем вы конструктор вашего плагина делаете свойством объекта jQuery?

                                                      $.selectik = function(element, options) {}
                                                      /* source code */
                                                      $.fn.selectik = function(options) {
                                                          return this.each(function() {
                                                              if (undefined == $(this).data('selectik')) {
                                                                  // create a new instance of the plugin
                                                                  var selectik = new $.selectik(this, options);
                                                                  $(this).data('selectik', selectik);
                                                              }
                                                          });
                                                      }
                                                      


                                                      2. Почему не храните методы в прототипе?

                                                      // public method: disable list
                                                      selectik.disableCS = function(){}
                                                      // public method: enable list
                                                      selectik.enableCS = function(){}
                                                      // public method: width of select
                                                      selectik.setWidthCS = function(width){}
                                                      
                                                        +1
                                                        За основу был взять шаблон для плагина Стефана Габоса. Мне понравилась идеи хранения методов и определение свойств именно в таком виде, поэтому и использовал.
                                                        Возможно в будущем будет переработана архитектура после всех доработок. Возьму на заметку!
                                                          +2
                                                          Ну если по поводу первого пункта еще можно поспорить: мало ли кто-то захочет запускать плагин как

                                                          $.selectik(element, { option1: 'option' });
                                                          

                                                          Но методы нужно конечно в прототип пихать. Они же общие для всех инстансов, зачем их дублировать для каждого селекта.
                                                          Когда будете пересматривать архитектуру, загляните сюда (если конечно еще не заглядывали).
                                                            +1
                                                            О! Спасибо за ссылку, обязательно изучу и постараюсь взять лучшее.
                                                        –1
                                                        Мне не нравится такие несемантичные списки. DIV-ами и LI-шками рисовать вручную контрол? Вы, что застряли во времена DOS, когда каждая программа рисовала контролы как могла? Это шаг в прошлое. Я против распространения таких методов.
                                                          0
                                                          1. Если дизайнер видет это не стандартно и пользователю будет удобно — то почему нет? Надо учитывать и тот фактор, что они будут выглядеть во всех браузерах одинаково.
                                                          2. Чем несемантичные?
                                                            –1
                                                            1. Пикассо тоже всякую хрень рисовал, и у него даже есть поклонники, но это не значит, что это хорошие картины. То, что видит дизайнер никого не волнует. Пользователю удобно было бы, и если бы производители браузеров и стандартов реализовали эти фичи с стандартном элементе управления. А делать костыль из-за чьей-то лени — это не престижно и унизительно. Логичнее для мировой справедливости заставить лентяев из W3C быстрее работать. Одинаковое отображение во всех браузерах опять-таки зависит от того, как мировое население сумеет заставить программистов браузеров хорошо работать, а не пить кофе с печеньками.
                                                            2. Несемантичные тем, что парсеры таких списков писать неудобно — везде свои. Да и представьте, если бы программы для Windows сами рисовали на окне элементы управления. Что было бы?! Да даже GetWindowText не сработал бы! У каждой программы был бы свой GetWindowText.

                                                            Поставьте, пожалуйста, мне плюсов в карму за выражение моего собственного мнения отличного от мнения большинства.
                                                          +2
                                                          '+valueOption+'

                                                          Это жесть. Почему вместо текста option используется его value? Получается, я не могу использовать список вида:

                                                          По-умолчанию
                                                          Красный
                                                          Зеленый
                                                          Синий

                                                            +2
                                                            Блин, хабратеги, первый раз использую… =(

                                                            В общем суть, что натравив на селект, где value отличается от text опшена, мы получаем, что в тексте вашего селекта отображается именно value…
                                                              0
                                                              Спасибо, суть понята. Уже написали на GitHub. Все фиксы вместе соберу и обновлю в ближайшие дни.
                                                            0
                                                            FireFox 12.0, селект разворачивается вверх.
                                                              0
                                                              Подскажите пожалуйста, как обрабатывать события вроде onchange?
                                                              Например, для цепочки селектов, или отправки формы сразу по смене, и т.п.
                                                              В демо и API такого примера не нашёл

                                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                              Самое читаемое