Полная кастомизация select без использования JS
Сколько я не мучил поисковик, а решения этого вопроса так и не нашлось. Конечно, всегда можно использовать JS и это нормально, но иногда заказчик душа просит изысков.В заголовке я несколько приврал: всем известно, что select полностью кастомизировать нельзя, поэтому мы будем имитировать select. Сделаем мы это с помощью нескольких radio, нескольких label, одного checkbox и одного div. Не так уж и много, правда? Структура
/** Это обещанный placeholder **/ label.selectGeneral: before { content: attr (placeholder); /** Взять текст из атрибута placeholder **/ display: inline-block; position: absolute; top: 0; left: 0; z-index: -1;
max-width: 100%;
text-align: left; white-space: nowrap; /** Не переносить слова **/
color: #adadad;
overflow-x: hidden; /** Скрыть лишнее **/ }
/** А это стрелочка **/ label.selectGeneral: after { content:»<>»; display: inline-block; position: absolute; top: 0; right: 0;
text-align: center;
background-color: #ffffff;
transform: rotate (90deg); }
label.selectGeneral input, label.selectGeneral label { display: none; }
label.selectGeneral div { min-width: 100%; max-height: 500 px; /** Ограничения на высоту списка выборов **/
overflow-x: hidden; } Осталось добавить немного магии — реализовать поведение всего этого добра. Магия будет основана на соседних селекторах и : checked у radio/checkbox. Выбранный элемент виден всегда и при закрытом состоянии select перекрывает собой placeholder. При открытии select, показываются все остальные элементы для выбора, а wrapper, в который они вложены, немного съезжает вниз, что-бы было видно placeholder и пользователь не забыл, что же он, собственно, выбирает.Поведение /** Если наш альтернативный select открыт, то wrapper **/ label.selectGeneral input[type=«checkbox»]: checked ~ div { position: absolute; /** приобретает абсолютную позицию **/ top: <высота label.selectGeneral>; /** и смешается немного вниз, открывая placeholder **/
overflow-y: auto; }
/** Все label внутри wrapper’а при открытом select **/ label.selectGeneral input[type=«checkbox»]: checked ~ div > label, /** И выбранный вариант **/ label.selectGeneral input[type=«radio»]: checked + label { display: block; /** должны быть видимыми **/ }
/** Подсветим выбранный вариант **/ label.selectGeneral input[type=«checkbox»]: checked ~ div > input[type=«radio»]: checked + label, /** и элемент на который наведена мыль при открытом selec **/ label.selectGeneral input[type=«checkbox»]: checked ~ div > label: hover { background-color: #ffa834; }
/** При закрытом select, нужно делегировать событие клика мышью с выбранного элемента родительскому label **/ label.selectGeneral input[type=«checkbox»]: not (: checked) ~ div > input[type=«radio»]: checked + label { position: relative; z-index: -1; } В конце применен трюк с z-index, который позволяет расположить дочерний элемент ниже (глубже по z-оси) родительского. Этот замечательный факт позволяет делегировать реакцию на клик по выбранному элементу нашему select’у, что бы он раскрылся.Рабочий пример можно лицезреть тут.
Из плюсов подхода можно отметить:
Кроссбраузерность — это работает везде, где работают label Относительная легкость — код не перенасыщен лишними элементами Возможность полной кастомизацией — стилизуется каждая мелочь Гибкость — не придется дописывать новых стилей при добавлении пунктов выбора Конечно же, есть и минусы, куда же без них: Отсутствие деградации — если не поддерживается, то стандартный select не спасет ситуацию Легкость легкостью, а дополнительный код все таки будет Невалидный код — div внутри label и атрибут placeholder у нее же, это не по стандарту Это не совсем минус, но эта штука не захлопывается сама после выбора Не думаю, что кто-то станет использовать это в production, но подход явно имеет право на жизнь.