Я (и, как мне кажется, многие из вас) сталкивался не раз с несовместимостью селектов с дизайном сайта. Боль состоит в том, что их нельзя стилизовать, а в каждом браузере они выглядят по-своему.
Конечно, есть огромное количество решений, представляемых фреймворками/библиотеками (тот же бутстрап). Но все они предполагают наличие JSа. Разумеется, в этом нет ничего страшного/плохого, но я попробовал сделать стилизуемый селект без JS в качестве фоллбэка на случай, если js по каким-либо причинам сломается.
Select
Выбор инструментов
Под инструментами в данном контексте я подразумеваю то, чем мы будем заменять селект. И мой выбор пал на radio кнопки по причине их схожего поведения: можно выбрать одновременно только один вариант.
....
Мы обернули радио-кнопки в , чтобы не городить ненужных IDшников и for-ов.
Логика
Для реализации выбора нужного пункта как в селекте нам надо, чтобы он оказывался в верхней позиции списка. Для этого обозначим, что значение выбранного элемента должно позиционироваться абсолютно (вырываться из потока) и помещаться на самый верх:
Основная часть работает — при выборе пункта он оказывается на лидирующей позиции. Осталось озаботиться тем, чтобы в «спокойном» состоянии был виден только выбранный пункт, а остальной список раскрывался при клике, также при клике в «молоко» список должен закрываться без изменения значения.
На ум приходит манипуляция псевдоселектором : focus. Добавим какой-нибудь инпут, который будет под него попадать. Я выбрал [type=text] потому, что ему можно задать размер (растянуть на всю ширину и высоту) и заслонить им лидирующий выбранный элемент.
....
Скрывать выпадающий список будем ограничением высоты и overflow: hidden:
Разумеется, инпута не должно быть видно (как и радио-кнопок, представляющих опции):
#dropdown input{
opacity: 0;
}
Замечание!
Следует использовать opacity: 0; вместо display: none; по причине того, что у срытых элементов (visually: hidden в том числе) не может быть состояния : focus.
Грязный хак
И когда уже казалось, что все получилось, я столкнулся с неожиданностью: при клике по пункту меню из списка фокус с управляющего инпута уходит быстрее, чем срабатывает клик на элементе списка. То есть происходит ровно то же, чтои при клике в молоко — список просто закрывается.
Чтобы увеличить задержку до скрытия выпавшего списка, придется использовать грязный хак:
Готово, смотрите пример (я добавил немного стилей для красоты): https://jsfiddle.net/2k1pvbyt/
Мультиселект
Если мы пойдем дальше, то нам захочется сделать таким же образом (без JS) мультиселект. Не каждый интегратор jquery-плагинов такой сделает с JS (JQuery), а мы-то с вами ишь чего вздумали! Ну сказано — сделано, нельзя упасть лицом в грязь. Попробуем разобраться, возможно ли это. И, если нет, что в каком именно моменте нельзя обойтись без js.
Что нам нужно изменить в предыдущем примере, чтобы наш селект стал мульти? Каждый пункт должен иметь возможность быть выбранным в не зависимости от других. Очевидно, нам надо сменить радио-кнопки на чекбоксы:
Да, это еще не все, и required там не случайно, с его помощью мы будем манипулировать нашим списком.
Если мы обернем это в , то у нас появится возможность манипулировать псевдоселекторами : valid / : invalid.
Заметка
, как и матчится на селектор :valid в том случае, если внутри него все поля также :valid
Чтобы понять, что именно мы должны сделать, надо четко сформулировать задачу:
Пусть выделенный пункт будет в начале списка. На одной строке с ним прочие выделенные пункты.
Поместить в начало списка выбранный элемент несложно, мы зададим родителю display: flex и будем играться со значением order:
У нас не получилось выяснить, где (в рамках задачи) мы не можем обойтись без js.
Возможно (точно), на более сложных примерах так и будет.
Дублирую ссылки на примеры:
Селект
Мультиселект
Ну и для тех, кто не хочет писать эту «кучу разметки» руками, накидал скрипт, который сделает это за вас.
Селект
Мультиселект
Дополнения и прочие issue приветствуются, без сомнения!
UPD
Пришла в голову идея использовать управляющий инпут. Он пригодится, если мы отойдем от концепции nojs, будем использовать некий js-конструктор для инициализации.
Среди внесенных изменений:
При фокусе инпут мы не скрываем, а смещаем ниже лидирующего пункта
Под это дело увеличиваем «вакантное место»
При инициализации вешаем обработчик на этот инпут, который на событие input генерирует css-строку, необходимую для фильтрации
» Смотреть пример
» Инструкция и код
Комментарии (4)
boodda
30 октября 2016 в 22:41
0
↑
↓
Пример на JS fiddle как то совсем плохо работает под хромом. Выбирает далеко не с первого раза и даже не со второго.
seokirill
30 октября 2016 в 23:25
0
↑
↓
Скорее всего, медленно нажимаешь мышкой.
Поиграйся, настрой побольше задержку или шустрей тыкай пальцем.
За время задержки ты должен успеть нажать на кнопку и поднять палец, так сказать, совершить полный цикл клика.
Akdmeh
31 октября 2016 в 00:03
0
↑
↓
Извините, у меня не сработало (Chrome, тачпад). Так и представил, что пишу пользователю:
«Если не выбрался select — попытайтесь шустрей тыкать пальцем».
Попытался несколько раз.
Сама идея очень интересная, но нерабочая…
seokirill
31 октября 2016 в 00:19
0
↑
↓
Это proof-of-concept, настройте под ваши нужды.
Вынес задержку в отдельную переменную, чтоб вам было проще. https://jsfiddle.net/2k1pvbyt/2/