[Select-Form]: Пишем свой select-список, используя jQuery и CSS

Здравствуйте, хабраюзеры и просто читающие. Сравнительно недавно задался вопросом, как применить стили к тегу 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: Надеюсь, что данная статья кому-то поможет в решении данного/похожего вопроса.
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 38

    +5

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


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


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


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

      +1
      спасибо за ваш комментарий, все учту! :)
      0
      В вашем случае получается, что каждый раз нужно описывать кастомный селект довольно сложно, почему бы не сделать тогда уж трансформацию стандартного в то, что вы описываете? И я бы порекомендовал использовать классы, вместо id, чтобы использовать селект несколько раз на странице.
        0
        кстати, да, на тот момент не задумался, что лучше сделать класс, буду исправляться, спасибо! :)
        +2
        Велосипед, и боль у тех, кто отключил скрипты. Лучше делать стандартные select-ы, и преобразовывать их в кастомные при загрузке.
          0
          интересно, много таких умников в интернете, кто отключает скрипты и радуется неработающим сайтам?
            0
            Тоже хочу на таких посмотреть
          0
          а как отображается выпадающее меню когда в низу страницы расположен контрол или когда места не хватает т.к. маленький экран?
            0
            Фигня там получается. Аккордеонит его
            0
            Код приведен не полностью, сбросов отступов нет у списка, например.
            Самое нехорошее — то, что этот контрол — не элемент формы, для проброса значений надо использовать скрытое поле (в примере нет). На мобильных работает странно
              0

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

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

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

                    0
                    Ну комбо-бокс это, конечно, совсем другое, тут нативных вариантов нету, кроме type=search
              +1
              https://codepen.io/bephf/pen/ogNBYW
                0

                Тогда уж...


                <label tabindex="-1">
                    <select>...</select>
                </label>
                0
                Пишем свой select-список, используя jQuery

                Заминусовал

                  +2
                  Прям сборник антипаттернов всего в трех файлах.
                    +5
                    Ну и в копилочку — нет управления с клавиатуры.
                      –2
                      В 2016м никто не пишет на jquery
                        0
                        А вот это было больно :)
                      • UFO just landed and posted this here
                          0
                          кроме эффектных div-оберток для тега select ничего не нашёл

                          И внезапно изобрел ухудшенную версию http://jqueryui.com/selectmenu/ :)
                            0
                            надо же с чего-то начинать :)
                            0
                            В работе встречал довольно старый проект cuSel, правда, он уже довольно давно заброшен.
                            С плюсов было то, что он брал существующие select-формы и перерабатывал их сам.
                            Подумайте над этим направлением, а не требовать создавать собственную верстку для HTML
                              0
                              Код, конечно, трешовый, но спасибо за то, что навели на мысль изобрести свой вариант этого велосипеда. По работе часто нужен кастомный селект, а тащить ради этого jqueryui ил другой плагин неохота.
                              Буду благодарен, если кто-то подскажет, какие требования и «фичи» необходимы для такого плагина.
                                0
                                конечно, трешовый, не буду спорить.
                                нужно подключить библиотеку jQuery и файл css, где описаны стили к соответствующим элементам.
                                  0
                                  Проблема тут не столько в jQuery и CSS. Основные минусы тут указали и не раз: невозможность повторного использования на одной странице, специфичная для данного плагина вёрстка, ну и неряшливое оформление кода.
                                  Я, как и писал, начал баловаться с написанием своего велосипеда. У того, что сделал, логика такая:
                                  • класс присваивается нативному селекту
                                  • плагин оборачивает его блоком и в него же (после селекта) добавляет свой сгенерированный html
                                  • селект прячется
                                  • при выборе элементов в кастомном селекте, они применяются и к скрытому нативному

                                  Плюсы следующие: возможность повторного использования, а так же при краше JS, остаётся нативный селект и ничего страшного не происходит. Из настроек пока добавил булевскую переменную, отвечающую за показ в кастомном меню disabled элементов.
                                +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
                                  0
                                  А такой же пример для древовидного списка можно выложить?
                                    0
                                    С древовидным списком подобного не делал, но можно попробовать. В принципе, вы можете сделать лучше :)
                                      0
                                      Конечно в теории каждый может сделать лучше. Я думал — может уже есть. Обычных списков много — деревьев мало.
                                    0
                                    Чем не подошел Select2?
                                      0
                                      Овер6к строк кода, 72 КБ в минифицированном виде. Кто ещё думает, что Селект — простой элемент?
                                      0
                                        0
                                        Не заработало в FF
                                        0
                                        Здравствуйте, а в каком файле сам select? Не нашел что-то.
                                          0
                                          за такой трэшак уже инвайты раздают, совсем хабр загнулся.
                                          чел сделал раскрывашку блока на jQuery, причем тут select, на который нельзя повесить этот чудо-плагин, совсем непонятно

                                          Only users with full accounts can post comments. Log in, please.