Как стать автором
Обновить

Кастомизируем select на чистом css

Время на прочтение4 мин
Количество просмотров98K
Как-то вечером я убивал время, читая статьи в интернете, и наткнулся на вот этот хабропост пользователя Cyapa, где расписано, как кастомизировать select на чистом css. В процессе просмотра данного решения нашел несколько весьма неудобных моментов, которые постарался исправить в своем решении этой задачи. Итак, приступим.

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

В своем варианте кастомизации я, так же как и предыдущий автор, использовал label, input[type=«radio»] и мощь css-селекторов. Так как сам селект средствами css полностью кастомизировать нереально, я имитировал поведение селекта.

Для начала, я накидал вот такую разметку:

Показать html код
<!-- Лейбел, при клике на который открывается селект -->
<label for="select" class="select">
    <!-- Этот инпут отвечает за закрытие селекта при клике за его пределами. Также этот инпут является стандартным значением нашего селекта -->
    <input type="radio" name="list" value="not_changed" id="bg" checked />
    <!-- Этот инпут является переключателем селекта в состояние "открыт" -->
    <input type="radio" name="list" value="not_changed" id="select">
    <!-- Этот лейбел используется для создания подложки, клик по которой приводит к закрытию селекта -->
    <label class="bg" for="bg"></label>
    <!-- Этот див - список параметров селекта, где #text - то, что выводится, когда ничего не выбрано -->
    <div class="items">
      <!-- Инпут, при клике на который происходит выбор параметра и сворачивание селекта -->
      <input type="radio" name="list" value="first_value" id="list[0]">
      <!-- Название параметра -->
      <label for="list[0]">First option</label>
      <!-- Инпут, при клике на который происходит выбор параметра и сворачивание селекта[1] -->
      <input type="radio" name="list" value="second_value" id="list[1]">
      <!-- Инпут, при клике на который происходит выбор параметра и сворачивание селекта[1] -->
      <label for="list[1]">Second loooooong option</label>
      <!-- Текст селекта по умолчанию. Выводится тогда, когда ничего не выбрано -->
      <span id="text">Select something...</span>
    </div>
</label>


При просмотре данного кода у вас мог возникнуть вопрос: «Почему у всех инпутов одинаковое имя?». Отвечу сразу: это сделано для того, чтобы наш селект адекватно вел себя(открывался и закрывался тогда, когда нужно). Но обо всем по порядку. Давайте перейдем к самой интересной, на мой взгляд, части — css.

Показать css код
/* скрываем все инпуты, чтобы все выглядело красиво */
input
{
  display: none;
}

/* стилизуем стандартный текст лейбела(желательно смотреть этот стиль после .items) */

#text
{
  position: absolute;
  display: block;
  top: 0;
  padding-left: 10px;
}

/* Задаем параметры нашего селекта - ширину, высоту и line-height(для центрирования текста по вертикали;этот парметр меньше ширины на 4px, т.к. в нашем блоке есть border размером в 2px со всех сторон) */

.select
{
  display: inline-block;
  width: 160px;
  height: 34px;
  line-height: 30px;
  position: relative;
}

/* Это наша стрелочка, показывающая, что селект можно раскрыть */

.select:before
{
  content: ">";
  display: inline-block;
  background: white;
  position: absolute;
  right: -5px;
  top: 2px;
  z-index: 2;
  width: 30px;
  height: 26px;
  text-align: center;
  line-height: 26px;
  border: 2px solid #ddd;
  transform: rotate(90deg);
  cursor: pointer;
}

/* Если ничего не выбрано, то наш изначальный текст черного цвета, как и должно быть */

.select input[name="list"]:not(:checked) ~ #text
{
  color: black;
  background: white;
}

/* Если же что-то выбрано, то наш текст становится невидимым и встает сверху выбранного параметра, чтобы при клике на него можно было заного открыть селект, что не было реализовано прошлым автором */

.select input[name="list"]:checked ~ #text
{
  background: transparent;
  color: transparent;
  z-index: 2;
}

/* Стилизация выключенного селекта */

#select:disabled ~ .items #text
{
  background: #eee;
}

/* Стилизация блока с опциями. min-height сделана для фикса высоты при абсолютном позиционировании, overflow же сделан для фиксированной высоты(см. ниже) */

.items
{
  display: block;
  min-height: 30px;
  position: absolute;
  border: 2px solid #ddd;
  overflow: hidden;
  width: 160px;
  cursor: pointer;
}

/* Если наш селект закрыт, то он имеет высоту 30px(сделано для того, чтобы слишком большие надписи не растягивали его в высоту)  */

#select:not(:checked) ~ .items
{
  height: 30px;
}

/* Все лейбелы(названия опций) изначально скрыты */

.items label
{
  border-top: 2px solid #ddd;
  display: none;
  padding-left: 10px;
  background: white;
}

/* Тут много объяснять не надо - просто выделение при наведении */

.items label:hover
{
  background: #eee;
  cursor: pointer;
}

/* Опять же фикс из-за абсолютного позиционирования */

#select:checked ~ .items
{
  padding-top: 30px;
}

/* Если наш селект открыт, то надо сделать все опции видимыми */

#select:checked ~ .items label
{
  display: block;
}

/* Если какая-либо опция была выбрана, то сделать ее видимой(при выборе селект автоматически закроется) */

.items input:checked + label
{
  display: block!important;
  border: none;
  background: white;
}

/* При открытии селекта создать подложку во весь экран, при клике на которую селект закроется, а значение останется пустым. background сделан для наглядности */

#select:checked ~ .bg
{
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  z-index: 0;
  background: rgba(0,0,0,0.4);
}


Вот она — самая интересная часть, в которой надо осмыслить как смена выбора инпута (все инпуты с типом радио имеют одинаковое имя => мы можем выбрать только один из них) влияет на наш селект. Еще одной особенностью этого варианта является возможность отключить селект, используя атрибут disabled на #select.

Готовый пример вы можете найти здесь.

Вот, собственно, и все. Парочка минусов предыдущего автора пофикшены, так что это решение, полагаю, на данный момент можно считать идеальным css-вариантом селекта. Надеюсь, мое решение кому-нибудь пригодится.
Теги:
Хабы:
Всего голосов 14: ↑12 и ↓2+10
Комментарии12

Публикации

Истории

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань