Здравствуйте, хабраюзеры и просто читающие. Сравнительно недавно задался вопросом, как применить стили к тегу select. Всем же хочется, чтобы стилизация формы соответствовала дизайну сайта, а пока что еще не все можно описать чистым CSS. В данной статье мы рассмотрим простенький пример написания своего select-списка, используя CSS и библиотеку языка JavaScript — jQuery. Думаю, особенно новичкам будет любопытен данный материал. Конечно, лучше было бы лучше написать на нативном JS, но всем известно, что строк кода было бы больше, и вряд ли он был бы понятнее.

Честно говоря, прежде чем взяться за создание очередного велосипеда, я пробовал найти подобное решение, но кроме эффектных div-оберток для тега select ничего не нашёл. И я подумал, что будет неплохо написать что-то простенькое и нужное. Ну, что ж, начнём!

В нашем кружке лепки из пластилина участвуют три файла:

  • selectbox.html
  • selectbox.css
  • selectbox.js

Рассмотрим их по очереди. Сначала обратим внимание на самое что ни наесть простое в этом примере — верстка списка или файл selectbox.html:

selectbox.html
<!-- /////////////////////////////////////////////////////////////////////////////////////////// 
      упустим описание начала документа и мета-тегов...  -->
 <!-- наш бокс со списком -->   
 <div id=selectBox>
       <!-- стрелка по правому краю для анимации, показывающая, что div-блок можно развернуть -->
      <img src="arrow.png" alt="" width='15px' class=arrow />
      <!-- текст, который будет виден в боксе -->
      <p class=valueTag name=select>Месяц</p>
        <!-- тот самый выпадающий список -->
       <ul id=selectMenuBox>
         <li class=option>Январь</li>
         <li class=option>Февраль</li>
         <li class=option>Март</li>
         <li class=option>Апрель</li>
         <li class=option>Май</li>
         <li class=option>Июнь</li>
         <li class=option>Июль</li>
         <li class=option>Август</li>
         <li class=option>Сентябрь</li>
         <li class=option>Октябрь</li>
         <li class=option>Ноябрь</li>
         <li class=option>Декабрь</li>
       </ul>
    </div> <!-- конец бокса -->
  </body>
</html>


Как видно из исходного html-кода, наш список будет предлагать нам выбрать месяц. Теперь рассмотрим файл selectbox.css:

selectbox.css
div#selectBox {
	width: 250px;
	position: relative;
	height: 50px;
	border-radius: 3px;
	border: solid 1px lightgrey;
	background-color: #fff;
	color: #333;
	cursor: pointer;
	overflow: hidden;
	transition: .3s;
}
div#selectBox p.valueTag {
	padding: 15px;
	cursor: pointer;
	transition: .2s;
	height: 40px;
}

div#selectBox > img.arrow {
	position: absolute;
	right: 0;
	width: 50px;

	padding: 15px;
}

/*
        для пользователей Safari, Chrome и Opera приятный бонус — стилизованный скролл-бар. 
*/
::-webkit-scrollbar {
	background: transparent;
	width: 0.5em;
	position: absolute;
}
::-webkit-scrollbar-track {
	background: transparent;
	position: absolute;
	z-index: -2;
}
::-webkit-scrollbar-thumb {
	border-radius: 100px;
	background: #888;
}

ul#selectMenuBox {
 background: #fff;
 transition: .3s;
 width: 100%;
 height: 200px;
 overflow-y: auto;
 overflow-x: hidden !important;
 position: absolute;
 margin-top: 00px;
 display: block;

}
ul#selectMenuBox > li {
	display: block;
	padding: 10px;
	border-radius: 00px;
	cursor: pointer;
}
ul#selectMenuBox > li.option {
	color: gray;
	padding: 10px;

}
ul#selectMenuBox > li.option:hover {
	color: #333;
	background: #e1e1e1;
	transition: .2s;
}


Особых сложностей тут нет, если вы владеете азами верстки и разметки средствами HTML и CSS3 соответственно.

А теперь к вкусностям! Рассмотрим исходный код плагина selectbox() для jQuery, файл selectbox.js:

selectbox.js
(function( $ ) {
  $.fn.selectbox = function() {
    
    // начальные параметры
    // задаем стандартную высоту div'a. 
    var selectDefaultHeight = $('#selectBox').height();
    // угол поворота изображения в div'e 
    var rotateDefault = "rotate(0deg)";
   
        // после нажатия кнопки срабатывает функция, в которой 
        // вычисляется исходная высота нашего div'a. 
        // очень удобно для сравнения с входящими параметрами (то, что задается в начале скрипта) 
        $('#selectBox > p.valueTag').click(function() {
          // вычисление высоты объекта методом height() 
          var currentHeight = $('#selectBox').height();
          // проверка условия на совпадение/не совпадение с заданной высотой вначале,
          // чтобы понять. что делать дальше. 
          if (currentHeight < 100 || currentHeight == selectDefaultHeight) {
              // если высота блока не менялась и равна высоте, заданной по умолчанию,
              // тогда мы открываем список и выбираем нужный элемент.
              $('#selectBox').height("250px");  // «точка остановки анимации»
              // здесь стилизуем нашу стрелку и делаем анимацию средствами CSS3 
              $('img.arrow').css({borderRadius: "1000px", transition: ".2s", transform: "rotate(180deg)"});
          }


         // иначе если список развернут (высота больше или равна 250 пикселям), 
         // то при нажатии на абзац с классом valueTag, сворачиваем наш список и
         // и присваиваем блоку первоначальную высоту + поворот стрелки в начальное положение
          if (currentHeight >= 250) {
            $('#selectBox').height(selectDefaultHeight);
            $('img.arrow').css({transform: rotateDefault});
          }
      });

     // так же сворачиваем список при выборе нужного элемента 
     // и меняем текст абзаца на текст элемента в списке
      $('li.option').click(function() {
        $('#selectBox').height(selectDefaultHeight);
       $('img.arrow').css({transform: rotateDefault});
        $('p.valueTag').text($(this).text());
      });
  };
})( jQuery );


Кода было больше, но удалось сжать благодаря методам css() и height(). Оформил в виде плагина для удобства и многократного использования. Можно сделать так, как вам нравится, лишь бы работало, так что я не обижусь, если кто-то оптимизирует мой костыль. Для вызова достаточно подключить внешний файл скрипта и вызвать плагин следующим образом:

$('selector').selectbox();

Предварительно, включив вызов в метод ready() объекта document, чтобы плагин загружался после полноценной загрузки документа. Подробнее, что такое плагин на jQuery, можно ознакомиться здесь.

Получилось что-то вроде этого:

image

Спасибо за внимание! Верстайте просто и со стилем!

P.S: Надеюсь, что данная статья кому-то поможет в решении данного/похожего вопроса.
Поделиться с друзьями
-->

Комментарии (38)


  1. k12th
    13.10.2016 11:45
    +5

    Делать реиспользуемый компонент и обращаться к нему по id — грубейшая ошибка и в стилях и в скриптах.


    Писать атрибуты без кавычек можно, но не стоит, особенно классы — их нередко надо добавлять-убирать. Ну а раз один атрибут пишем с кавычками, то и остальные лучше тоже — чтобы не задумываться каждый раз.


    Отступы в стилях поехали — где один пробел, а где один таб.


    В скрипте неконсистентные кавычки, где одинарные, где двойные — причем в одной строке. Выберите один стиль и соблюдайте его (хинт: большинство выбирает одинарные).


    1. naburamora
      13.10.2016 20:32
      +1

      спасибо за ваш комментарий, все учту! :)


  1. smurov
    13.10.2016 11:46

    В вашем случае получается, что каждый раз нужно описывать кастомный селект довольно сложно, почему бы не сделать тогда уж трансформацию стандартного в то, что вы описываете? И я бы порекомендовал использовать классы, вместо id, чтобы использовать селект несколько раз на странице.


    1. naburamora
      13.10.2016 20:33

      кстати, да, на тот момент не задумался, что лучше сделать класс, буду исправляться, спасибо! :)


  1. papikus
    13.10.2016 11:48
    +2

    Велосипед, и боль у тех, кто отключил скрипты. Лучше делать стандартные select-ы, и преобразовывать их в кастомные при загрузке.


    1. pashted
      13.10.2016 20:01

      интересно, много таких умников в интернете, кто отключает скрипты и радуется неработающим сайтам?


      1. sashabeep
        14.10.2016 11:37

        Тоже хочу на таких посмотреть


  1. Epsil0neR
    13.10.2016 11:53

    а как отображается выпадающее меню когда в низу страницы расположен контрол или когда места не хватает т.к. маленький экран?


    1. sashabeep
      13.10.2016 12:05

      Фигня там получается. Аккордеонит его


  1. sashabeep
    13.10.2016 11:57

    Код приведен не полностью, сбросов отступов нет у списка, например.
    Самое нехорошее — то, что этот контрол — не элемент формы, для проброса значений надо использовать скрытое поле (в примере нет). На мобильных работает странно


    1. jMas
      13.10.2016 12:23

      Хороший вариант, когда кстомный селект просто кладется рядом с существующим <select> (который хайдится) и просто меняет selected в нем, попутно триггря событие change. Да еще и слушает исходный <select> на предмет изменений <option> или value.


      1. sashabeep
        13.10.2016 14:00

        А еще «более лучший» вариант — не портить селект, картинку стрелки можно подменить, обводку сделать другую, этого достаточно почти всегда


        1. jMas
          13.10.2016 15:08

          Иногда нужно, когда обычного селекта не хватает. Например селект с поиском или placeholder-ом.


          1. sashabeep
            14.10.2016 11:35

            Ну комбо-бокс это, конечно, совсем другое, тут нативных вариантов нету, кроме type=search


  1. sashabeep
    13.10.2016 12:06
    +1

    https://codepen.io/bephf/pen/ogNBYW


    1. jMas
      13.10.2016 19:12

      Тогда уж...


      <label tabindex="-1">
          <select>...</select>
      </label>


  1. Ohar
    13.10.2016 12:54

    Пишем свой select-список, используя jQuery

    Заминусовал


  1. zxcabs
    13.10.2016 12:58
    +2

    Прям сборник антипаттернов всего в трех файлах.


  1. PaulZi
    13.10.2016 13:22
    +5

    Ну и в копилочку — нет управления с клавиатуры.


  1. xakepmega
    13.10.2016 14:04
    -2

    В 2016м никто не пишет на jquery


    1. sashabeep
      13.10.2016 14:20

      А вот это было больно :)


  1. KlimovDm
    13.10.2016 14:18

    >>> $('selector').selectbox();

    Коллега, поглядите внимательно на JS. Зачем вам селектор? Как следствие — зачем вам plugin jQuery?


  1. sashabeep
    13.10.2016 14:20

    кроме эффектных div-оберток для тега select ничего не нашёл

    И внезапно изобрел ухудшенную версию http://jqueryui.com/selectmenu/ :)


    1. naburamora
      13.10.2016 20:03

      надо же с чего-то начинать :)


  1. Akdmeh
    13.10.2016 14:21

    В работе встречал довольно старый проект cuSel, правда, он уже довольно давно заброшен.
    С плюсов было то, что он брал существующие select-формы и перерабатывал их сам.
    Подумайте над этим направлением, а не требовать создавать собственную верстку для HTML


  1. newkamikaze
    13.10.2016 14:45

    Код, конечно, трешовый, но спасибо за то, что навели на мысль изобрести свой вариант этого велосипеда. По работе часто нужен кастомный селект, а тащить ради этого jqueryui ил другой плагин неохота.
    Буду благодарен, если кто-то подскажет, какие требования и «фичи» необходимы для такого плагина.


    1. naburamora
      13.10.2016 20:06

      конечно, трешовый, не буду спорить.
      нужно подключить библиотеку jQuery и файл css, где описаны стили к соответствующим элементам.


      1. newkamikaze
        13.10.2016 20:43

        Проблема тут не столько в jQuery и CSS. Основные минусы тут указали и не раз: невозможность повторного использования на одной странице, специфичная для данного плагина вёрстка, ну и неряшливое оформление кода.
        Я, как и писал, начал баловаться с написанием своего велосипеда. У того, что сделал, логика такая:

        • класс присваивается нативному селекту
        • плагин оборачивает его блоком и в него же (после селекта) добавляет свой сгенерированный html
        • селект прячется
        • при выборе элементов в кастомном селекте, они применяются и к скрытому нативному

        Плюсы следующие: возможность повторного использования, а так же при краше JS, остаётся нативный селект и ничего страшного не происходит. Из настроек пока добавил булевскую переменную, отвечающую за показ в кастомном меню disabled элементов.


  1. Aingis
    13.10.2016 14:54
    +3

    Япона мать!
    Идентификатор для переиспользуемого компонента?

    Только на одном элементе:

    <img src="arrow.png" alt="" width='15px' class=arrow />
    прямо зоопарк какой-то: все виды записей атрибутов: без кавычек, с двойными и ординарными. Зачем-то закрывающий слеш, хотя без кавычек XHTML писать нельзя. Указана ширина, но не высота.

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

    В 2016 году ретина уже обыденность, а SVG исполнилось 15 лет.

    Отступы в HTML скачут.

    Кстати, если не пишете атрибуты в кавычках, то закрывающие теги </p> и </li> тоже можно не писать.

    div#selectBox

    Зачем div в селекторе с идентификтором? БЭМ изобрели уже почти 10 лет назад.

    Никаких «вкусностей» и близко в коде нет, банальные манипуляции скриптом. Лапша кода, достойная WTFJS. Состояние узнаётся из высоты элемента!
    // иначе если список развернут (высота больше или равна 250 пикселям)...
    if (currentHeight >= 250) { // ...
    Управление с клавиатуры отсутствует (стрелочки, Enter, Escape, ввод с клавиатуры значения из списка).

    Но главная ошибка в том, что <select> — это ужасный элемент сам по себе. Делать его ухудшенную копию — последнее что стоит делать.
    https://medium.com/apegroup-texts/why-drop-down-lists-are-bad-for-the-user-experience-eeda5cbbd315
    http://www.lukew.com/ff/entry.asp?1950


  1. x893
    13.10.2016 15:25

    А такой же пример для древовидного списка можно выложить?


    1. naburamora
      13.10.2016 20:36

      С древовидным списком подобного не делал, но можно попробовать. В принципе, вы можете сделать лучше :)


      1. x893
        13.10.2016 20:46

        Конечно в теории каждый может сделать лучше. Я думал — может уже есть. Обычных списков много — деревьев мало.


  1. SkyCat
    13.10.2016 18:25

    Чем не подошел Select2?


    1. Aingis
      14.10.2016 16:27

      Овер6к строк кода, 72 КБ в минифицированном виде. Кто ещё думает, что Селект — простой элемент?


  1. deamondz
    13.10.2016 20:07

    1. sashabeep
      14.10.2016 11:32

      Не заработало в FF


  1. ambientos
    13.10.2016 20:43

    Здравствуйте, а в каком файле сам select? Не нашел что-то.


  1. serginhold
    14.10.2016 20:56

    за такой трэшак уже инвайты раздают, совсем хабр загнулся.
    чел сделал раскрывашку блока на jQuery, причем тут select, на который нельзя повесить этот чудо-плагин, совсем непонятно