Честно говоря, прежде чем взяться за создание очередного велосипеда, я пробовал найти подобное решение, но кроме эффектных div-оберток для тега select ничего не нашёл. И я подумал, что будет неплохо написать что-то простенькое и нужное. Ну, что ж, начнём!
В нашем кружке лепки из пластилина участвуют три файла:
- selectbox.html
- selectbox.css
- selectbox.js
Рассмотрим их по очереди. Сначала обратим внимание на самое что ни наесть простое в этом примере — верстка списка или файл 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:
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:
(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, можно ознакомиться здесь.
Получилось что-то вроде этого:
Спасибо за внимание! Верстайте просто и со стилем!
P.S: Надеюсь, что данная статья кому-то поможет в решении данного/похожего вопроса.
Комментарии (38)
smurov
13.10.2016 11:46В вашем случае получается, что каждый раз нужно описывать кастомный селект довольно сложно, почему бы не сделать тогда уж трансформацию стандартного в то, что вы описываете? И я бы порекомендовал использовать классы, вместо id, чтобы использовать селект несколько раз на странице.
naburamora
13.10.2016 20:33кстати, да, на тот момент не задумался, что лучше сделать класс, буду исправляться, спасибо! :)
papikus
13.10.2016 11:48+2Велосипед, и боль у тех, кто отключил скрипты. Лучше делать стандартные select-ы, и преобразовывать их в кастомные при загрузке.
sashabeep
13.10.2016 11:57Код приведен не полностью, сбросов отступов нет у списка, например.
Самое нехорошее — то, что этот контрол — не элемент формы, для проброса значений надо использовать скрытое поле (в примере нет). На мобильных работает странноjMas
13.10.2016 12:23Хороший вариант, когда кстомный селект просто кладется рядом с существующим
<select>
(который хайдится) и просто меняетselected
в нем, попутно триггря событиеchange
. Да еще и слушает исходный<select>
на предмет изменений<option>
илиvalue
.sashabeep
13.10.2016 14:00А еще «более лучший» вариант — не портить селект, картинку стрелки можно подменить, обводку сделать другую, этого достаточно почти всегда
KlimovDm
13.10.2016 14:18>>> $('selector').selectbox();
Коллега, поглядите внимательно на JS. Зачем вам селектор? Как следствие — зачем вам plugin jQuery?
sashabeep
13.10.2016 14:20кроме эффектных div-оберток для тега select ничего не нашёл
И внезапно изобрел ухудшенную версию http://jqueryui.com/selectmenu/ :)
Akdmeh
13.10.2016 14:21В работе встречал довольно старый проект cuSel, правда, он уже довольно давно заброшен.
С плюсов было то, что он брал существующие select-формы и перерабатывал их сам.
Подумайте над этим направлением, а не требовать создавать собственную верстку для HTML
newkamikaze
13.10.2016 14:45Код, конечно, трешовый, но спасибо за то, что навели на мысль изобрести свой вариант этого велосипеда. По работе часто нужен кастомный селект, а тащить ради этого jqueryui ил другой плагин неохота.
Буду благодарен, если кто-то подскажет, какие требования и «фичи» необходимы для такого плагина.naburamora
13.10.2016 20:06конечно, трешовый, не буду спорить.
нужно подключить библиотеку jQuery и файл css, где описаны стили к соответствующим элементам.newkamikaze
13.10.2016 20:43Проблема тут не столько в jQuery и CSS. Основные минусы тут указали и не раз: невозможность повторного использования на одной странице, специфичная для данного плагина вёрстка, ну и неряшливое оформление кода.
Я, как и писал, начал баловаться с написанием своего велосипеда. У того, что сделал, логика такая:
- класс присваивается нативному селекту
- плагин оборачивает его блоком и в него же (после селекта) добавляет свой сгенерированный html
- селект прячется
- при выборе элементов в кастомном селекте, они применяются и к скрытому нативному
Плюсы следующие: возможность повторного использования, а так же при краше JS, остаётся нативный селект и ничего страшного не происходит. Из настроек пока добавил булевскую переменную, отвечающую за показ в кастомном меню disabled элементов.
Aingis
13.10.2016 14:54+3Япона мать!
Идентификатор для переиспользуемого компонента?
Только на одном элементе:
прямо зоопарк какой-то: все виды записей атрибутов: без кавычек, с двойными и ординарными. Зачем-то закрывающий слеш, хотя без кавычек XHTML писать нельзя. Указана ширина, но не высота.<img src="arrow.png" alt="" width='15px' class=arrow />
Непонятно, зачем вообще отдельный элемент, а не фоновая картинка или генерируемое содержимое в CSS.
В 2016 году ретина уже обыденность, а SVG исполнилось 15 лет.
Отступы в HTML скачут.
Кстати, если не пишете атрибуты в кавычках, то закрывающие теги</p>
и</li>
тоже можно не писать.
div#selectBox
Зачемdiv
в селекторе с идентификтором? БЭМ изобрели уже почти 10 лет назад.
Никаких «вкусностей» и близко в коде нет, банальные манипуляции скриптом. Лапша кода, достойная WTFJS. Состояние узнаётся из высоты элемента!
Управление с клавиатуры отсутствует (стрелочки, Enter, Escape, ввод с клавиатуры значения из списка).// иначе если список развернут (высота больше или равна 250 пикселям)... if (currentHeight >= 250) { // ...
Но главная ошибка в том, что<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
x893
13.10.2016 15:25А такой же пример для древовидного списка можно выложить?
naburamora
13.10.2016 20:36С древовидным списком подобного не делал, но можно попробовать. В принципе, вы можете сделать лучше :)
x893
13.10.2016 20:46Конечно в теории каждый может сделать лучше. Я думал — может уже есть. Обычных списков много — деревьев мало.
serginhold
14.10.2016 20:56за такой трэшак уже инвайты раздают, совсем хабр загнулся.
чел сделал раскрывашку блока на jQuery, причем тут select, на который нельзя повесить этот чудо-плагин, совсем непонятно
k12th
Делать реиспользуемый компонент и обращаться к нему по id — грубейшая ошибка и в стилях и в скриптах.
Писать атрибуты без кавычек можно, но не стоит, особенно классы — их нередко надо добавлять-убирать. Ну а раз один атрибут пишем с кавычками, то и остальные лучше тоже — чтобы не задумываться каждый раз.
Отступы в стилях поехали — где один пробел, а где один таб.
В скрипте неконсистентные кавычки, где одинарные, где двойные — причем в одной строке. Выберите один стиль и соблюдайте его (хинт: большинство выбирает одинарные).
naburamora
спасибо за ваш комментарий, все учту! :)