Пока смотрел код того поста, заметил, что автор сделал селект, который невозможно закрыть при клике вне его, а также невозможность открыть селект при клике на название элемента, если оно уже выбрано.
В своем варианте кастомизации я, так же как и предыдущий автор, использовал label, input[type=«radio»] и мощь css-селекторов. Так как сам селект средствами css полностью кастомизировать нереально, я имитировал поведение селекта.
Для начала, я накидал вот такую разметку:
<!-- Лейбел, при клике на который открывается селект -->
<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.
/* скрываем все инпуты, чтобы все выглядело красиво */
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-вариантом селекта. Надеюсь, мое решение кому-нибудь пригодится.
Комментарии (11)
dpr
07.03.2019 11:21Такое решение может пригодится разве что для списков с небольшим количеством опций выбора. Две-три, максимум пять. Длинные списки будут сильно уступать нативным в удобстве использования на мобильных телефонах.
artalar
07.03.2019 11:23-1Спасибо!
Может быть опубликуете тут? Было бы удобно: poet.codesZibx
07.03.2019 11:38Что было бы там удобно? Как вообще этим пользоваться?
artalar
07.03.2019 11:47Отдельно код, отдельно комментарии, при этом все удобно связанно: poet.codes/e/KMXQEO2gquN (наведите мышкой на выделенные слова).
Внизу главного лендинга есть видео как это делать. Что там может быть не понятно?
sfi0zy
07.03.2019 11:34Клавиатура не поддерживается, читалка бесится. Делать такие штуки интересно конечно, но если мы говорим о практическом использовании — не стоит забывать о пользователях. Они бывают разными и ваш селект для некоторых из них будет совсем недоступен. Так что стоит рассматривать это как упражнение, не более.
P.S.: Демки с codepen можно вставлять прямо в статью.JustDont
07.03.2019 12:52Вот да. Если у вас решение «на чистом цсс», но там всё равно нифига не работает (клавиатура, читалка, да вообще просто если список будет длинный, то всё умрёт) — то такие штуки в целом бессмысленны. Тут уже надо брать JS и делать полную имитацию нативного селекта с поддержкой всего, в них по крайней мере есть практический смысл (да и море уже готовых 3rd-party реализаций).
ThisMan
07.03.2019 13:47Нативный селект умеет правильно позиционировать выпадашку, в зависимости от того хватает для нее места или нет. Тут так не получится, и да, для одного селекта столько кода? Нет уж спасибо, где мой старый добрый
js
?
HawkeyePierce89
Грустно это всё, на самом деле. Что вроде бы фронтенд весь такой идёт вперёд семимильными шагами в светлое (не факт, но допустим) будущее, а такая давно нужная и необходимая штука, как стилизация нативного селекта до сих пор всеми игнорируется и никто ничего не думает по этому поводу из тех, кто отвечает за разработку веб-стандартов.
И приходится делать вот такие, если уж говорить прямо, костыли. Нет, подход интересный, но только на поиграться, потому что такую штуку использовать как замену нативного select нельзя.