Как-то вечером я убивал время, читая статьи в интернете, и наткнулся на вот этот хабропост пользователя Cyapa, где расписано, как кастомизировать select на чистом css. В процессе просмотра данного решения нашел несколько весьма неудобных моментов, которые постарался исправить в своем решении этой задачи. Итак, приступим.
Пока смотрел код того поста, заметил, что автор сделал селект, который невозможно закрыть при клике вне его, а также невозможность открыть селект при клике на название элемента, если оно уже выбрано.
В своем варианте кастомизации я, так же как и предыдущий автор, использовал label, input[type=«radio»] и мощь css-селекторов. Так как сам селект средствами css полностью кастомизировать нереально, я имитировал поведение селекта.
Для начала, я накидал вот такую разметку:
При просмотре данного кода у вас мог возникнуть вопрос: «Почему у всех инпутов одинаковое имя?». Отвечу сразу: это сделано для того, чтобы наш селект адекватно вел себя(открывался и закрывался тогда, когда нужно). Но обо всем по порядку. Давайте перейдем к самой интересной, на мой взгляд, части — css.
Вот она — самая интересная часть, в которой надо осмыслить как смена выбора инпута (все инпуты с типом радио имеют одинаковое имя => мы можем выбрать только один из них) влияет на наш селект. Еще одной особенностью этого варианта является возможность отключить селект, используя атрибут disabled на #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-вариантом селекта. Надеюсь, мое решение кому-нибудь пригодится.