Полная кастомизация select без использования JS

    imageСколько я не мучил поисковик, а решения этого вопроса так и не нашлось. Конечно, всегда можно использовать JS и это нормально, но иногда заказчик душа просит изысков.

    В заголовке я несколько приврал: всем известно, что select полностью кастомизировать нельзя, поэтому мы будем имитировать select. Сделаем мы это с помощью нескольких radio, нескольких label и одного div. Не так уж и много, правда?

    Структура
    <!-- Вот эта штука и заменит нам select -->
    <label class="selectGeneral" placeholder="select your OS..."> <!-- Да, да, placeholder тоже поддерживается -->
        <!-- Если этот radio выбран - select открыт, если нет - select закрыт -->
        <input type="radio" name="OS">
    
        <!-- Это wrapper для вариантов выбора -->
        <div>
            <!-- Группа radio и есть аналог оригинальных option -->
            <input 
                type="radio"
                name="OS"
                value="linux"
                id="OS[linux]"
            >
            <!-- Аналог видимой части option -->
            <label for="OS[linux]">linux</label> 
    
            <input
                type="radio"
                name="OS"
                value="windows"
                id="OS[windows]"
            >
            <label for="OS[windows]">windows</label>
    
            <input
                type="radio"
                name="OS"
                value="other"
                id="OS[other]"
            >
            <label for="OS[other]">other</label>
        </div>
    </label>
    


    Корневая label будет всегда видимой частью нашего альтернативного select. При клике на нее будет переключаться основной radio, который и отвечает за состояния открыт/закрыт у этой конструкции. Placeholder и традиционная стрелочка будут реализованы через псевдоэлементы :before и :after у корневой label. Все остальное, кроме wrapper (тот самый единственный div), по умолчанию скрыто. Почему мы не скрываем wrapper? Потому что в нем находится выбранный элемент (если такой есть), а он должен быть виден всегда.

    Основная часть
    label.selectGeneral
    {
        display: block;
        position: relative;
    }
    
    /** Это обещанный 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: 500px; /** Ограничения на высоту списка выборов **/
    
        overflow-x: hidden;
    }
    


    Осталось добавить немного магии — реализовать поведение всего этого добра. Магия будет основана на соседних селекторах и :checked у radio. Выбранный элемент виден всегда и при закрытом состоянии select перекрывает собой placeholder. При открытии select, показываются все остальные элементы для выбора, а wrapper, в который они вложены, немного съезжает вниз, что-бы было видно placeholder и пользователь не забыл, что же он, собственно, выбирает.

    Поведение
    /** Если наш альтернативный select открыт, то wrapper **/
    label.selectGeneral input[type="radio"]:checked ~ div
    {
        position: absolute; /** приобретает абсолютную позицию **/
        top: <высота label.selectGeneral>; /** и смешается немного вниз, открывая placeholder **/
    
        overflow-y: auto;
    }
    
    /** Все label внутри wrapper'а при открытом select **/
    label.selectGeneral input[type="radio"]:checked ~ div > label,
    /** И выбранный вариант **/
    label.selectGeneral input[type="radio"]:checked + label
    {
        display: block; /** должны быть видимыми **/
    }
    
    /** Подсветим элемент на который наведена мышь при открытом select **/
    label.selectGeneral input[type="radio"]:checked ~ div > label:hover
    {
        background-color: #ffa834;
    }
    
    /** При закрытом select, нужно делегировать событие клика мышью с выбранного элемента родительскому label **/
    label.selectGeneral input[type="radio"]:not(:checked) ~ div > input[type="radio"]:checked + label
    {
        position: relative;
        z-index: -1; 
    }
    


    В конце применен трюк с z-index, который позволяет расположить дочерний элемент ниже (глубже по z-оси) родительского. Этот замечательный факт позволяет делегировать реакцию на клик по выбранному элементу нашему select'у, что бы он раскрылся.

    Рабочий пример можно лицезреть тут.

    Из плюсов подхода можно отметить:
    • Кроссбраузерность — это работает везде, где работают label
    • Относительная легкость — код не перенасыщен лишними элементами
    • Возможность полной кастомизацией — стилизуется каждая мелочь
    • Гибкость — не придется дописывать новых стилей при добавлении пунктов выбора

    Конечно же, есть и минусы, куда же без них:
    • Отсутствие деградации — если не поддерживается, то стандартный select не спасет ситуацию (как подсказывают комментаторы — очень важный пункт для портативных устройств)
    • Легкость легкостью, а дополнительный код все таки будет
    • Невалидный код — div внутри label и атрибут placeholder у нее же, это не по стандарту
    • Это не совсем минус, но эта штука не захлопывается сама при клике в любом месте экрана


    Не думаю, что кто-то станет использовать это в production, но подход явно имеет право на жизнь.

    UDPATE: Изменен код — добавлена функция автоматического закрытия select при выборе.

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 90

    • UFO just landed and posted this here
      • UFO just landed and posted this here
        • UFO just landed and posted this here
          +16
          Только ситхи всё возводят в абсолют.
          • UFO just landed and posted this here
              +7
              Серьёзными исследованиями селекта? Дожили (((
                +7
                А вот это вообще мрак:
                image

                На этой планете нет человека который бы смог сделать не глючащий удобный дропдаун для мобильных устройств, лучше нативного селекта.
                • UFO just landed and posted this here
                    +19
                    Тысячи пунктов в меню? И вы все еще считаете что дело в селектах? У меня для вас плохие новости.
                    • UFO just landed and posted this here
        • UFO just landed and posted this here
            –1
            более того: клик во втором селекте меняет значение первого, а не своего селекта.
              +1
              Это из-за того, что предыдущий комментатор не изменил id и name у второй radio-группы.
              0
              Забыл указать z-index для div. Спасибо, исправил.
              0
              codepen.io/anon/pen/qEmNjb — в сафари выбор элемента не работает. Сколько не кликай по windows/linux/other ничего не происходит
              +17
              В жизни оно, конечно, неюзабельно совершенно, но как эксперимент — интересно.
                0
                Это да. Возможно, ситуация бы изменилась, если бы удалось прикрутить автоматическое захлопывание при выборе.
              0
              Почему-то для того чтобы сделать выбор и селект схлопнутся, нужно ткнуть именно в поле ввода(верхнюю строку). Я уж думал не работает, случайно ткнул — сработало. Fx 35. Схлопывалась бы хоть по щелчку в любом другом месте.
                0
                Да, я это упомянул в минусах. Возможно, у кого-то есть решение этой проблемы?

                Кстати, это можно рассматривать как фичу: не закрывается для того, что бы не сбиться случайно :)
                  0
                  Ясно, просто не совсем так понял это примечание.
                    +1
                    Пораскинул мозгом и таки удалось исправить этот момент — вот.

                    Отредактировал пост.
                0
                К сожалению, кастомные решения для select'ов становятся огромной головной болью, когда речь заходит о поддержке портативных устройств.
                  0
                  На мой взгляд, для портативных устройств нужно более тщательно продумывать кастомизацию или писать отдельную.

                  Например, если переопределить размеры select из статьи через rem, а сам размер шрифта задавать в зависимости от разрешения экрана через @meadia запросы, то должно получиться вполне сносно.
                    +7
                    Для портативных устройств должен работать обычный селект.
                  0
                  Плохое решение. В JS для получения значения у вас будет набор радио, а не обычный селект.
                  Плюс, проблемы с деградацией на мобильниках и пр, как справедливо отмечают выше.
                    0
                    Добавил этот пункт в пост. Спасибо.
                  • UFO just landed and posted this here
                      0
                      Почему же IE не оценит? IE очень даже оценил. В 9 все работает, в младших версиях проверить не было возможности, но, если там работает for для label, то все будет работать.

                      Багов не так уж и много. Из ощутимых только то, что не сворачивается select при клике мимо него.
                    0
                    В Opera 12 не работает: список разворачивается, опции подсвечиваются, но на клики не реагируют.
                      +4
                      К недостаткам: невозможно использовать с клавиатуры. Без JS устранить этот недостаток, наверное, нельзя.
                        0
                        По мне так этот недостаток самый весомый :)
                        Ломать стандартное поведение контролов кривыми костылями, за такое не знаю что надо делать…
                        Сейчас уже проще всего кастомизировать нераскрытый селект через appearance: none (кроме IE конечно).
                        • UFO just landed and posted this here
                        +3
                        Делал подобный селект с учётом того, чтобы закрывался при потере фокуса.
                        codepen.io/felixexter/pen/RNGeap
                        Есть свои минусы, например, зависит от количества, но если опций немного, то решение вполне годное.
                          –1
                          > В заголовке я несколько приврал: всем известно, что select полностью кастомизировать нельзя, поэтому мы будем имитировать select.
                          В этой разнице и был весь смысл.

                          Реализаций кастомного селекта так много, что пора писать комментарий «каждый программист на javascript должен написать свою реализацию кастомного селекта ©» с миллионом ссылок.

                          А вообще Webkit уже давно ввёл какие-то возможности для кастомизации checkbox и radiobox, в FF тоже что-то такое было, так что не удивлюсь, если кастомизировать селект можно (им. в виду полностью).
                            +2
                            В том то и дело, что имитация без использования JS. С JS любой дурак может. :)

                            По поводу кастомизации checkbox/radio — единственный адекватный вариант это сделать, все так же через label и :checked.
                            Возможности кастомизировать select до сих пор не появилось. Добавился appearance: none, который позволяет кастомизировать нераскрытый select, но стилизация пунктов стоит на месте.
                              0
                              Да, в самом деле. Прошу прощения, день сложный был :).
                              Вы правы.
                            0
                            А есть пример стилизации обычного списка ul под выпадающий список аля select?
                              0
                              tympanus.net/codrops там наверняка
                                0
                                Все тоже самое, что и в примере, только label'ы будут внутри li. :) Ну и селекторы немного подправить.
                              0
                              Упущена одна вещь, если открыть выпадающий список, а потом щёлкнуть где-нибудь рядом, то в нормальных системах список закрывается, тут же он висит открытым.
                              Ещё в 35 лисе не работает раскрытие по общему телу где текст, только по значку сбоку.
                                +1
                                Вот за это я не люблю веб-разработку — вместо того чтобы заниматься вещами достойными программиста, приходится иметь дело со всякой херней типо кастомных селектбоксов.
                                • UFO just landed and posted this here
                                  0
                                  При открытии списка выбор становится обязательным действием. Иначе список не закроется.
                                    0
                                    Можно добавить пустой пункт.
                                    +2
                                    А старый добрый трюк с наложением прозрачного селекта на стилизованную подложку уже не котируется?
                                      –5
                                      Кроссбраузерность — это работает везде, где работают label

                                      Давно у нас кроссбраузерность 9+? Кроссбраузерность вроде IE6+. в край IE8+, но во втором случае не упоминают кроссбраузерность, а так и пишут IE8+.

                                      Судя по комментариям много где не работает. Вы бы хотя бы, ради любопытства, сравнили бы с таблицей на caniuse.com

                                      Относительная легкость — код не перенасыщен лишними элементами

                                      Лишний элемент <input type="radio" name="OS">, который вносит свои лишние программные корректировки на стороне сервера.

                                      Гибкость — не придется дописывать новых стилей при добавлении пунктов выбора

                                      А вот тут начинаются проблемы, если необходимо будет использовать стандартные возможности select — accesskey, autofocus, required, а так же не предусмотрен optgroup, хотя его и можно легко реализовать (для тех кто может легко — статья не интересна, для тех, кому интересна статья — легко не сможет ).

                                      В целом данный способ ничего нового не несёт, в нем скомбинированы выпадающее меню и кастомизация input (checkbox и radio). Но может вызвать множество головной боли. Если надо кастомизировать select — лучше воспользоваться старым добрым способом — сокрытие стрелки и создание своей. Остальное всё вполне разукрашивается и даже более «кроссбраузерно», чем данный пример и имеет все преимущества и нет недостатков данного примера.

                                      На скорую руку пример jsfiddle.net/BaNru/9njyweL0/
                                        0
                                        А что там за background-image встроен?
                                          0
                                          Картинка в base64 (стрелочки), разумеется причина отличается от тех, что описаны в статье, сделано чтобы не ссылаться на сторонние ресурсы и разумеется можно заменить на любой путь до картинки.
                                          +1
                                          Необходимый уровень поддержки IE — это экономическая метрика: доходы от пользователей древней версии IE должны превышать расходы на поддержку этого хлама. Очень сильно зависит от специфики проекта. Где-то надо IE6+, а где-то и IE10+ достаточно.

                                          А так, этот пост, конечно, скорее интересное упражнение, чем что-то пригодное для production. :)
                                            –1
                                            Где я писал про экономические метрики и доходы (или упоминал в данном ключе)? При чем тут это?!

                                            Автор заявил, что его решение кроссбраузерное, а это явная ложь. И не важно — надо кому-то IE6, тем более если это не для production.

                                            Если верстальщик подобное решение не знает (хотя бы раздельно), то это плохой верстальщик.
                                              0
                                              А почему именно IE6? Почему не IE4?
                                                0
                                                Ответил ниже
                                                  0
                                                  IE8 не актуальный, XP больше не поддерживаемая платформа. Те кто на ней сидят уже используют альтернативные обозреватели если совсем не дураки.
                                                    0
                                                    в край IE8+, но во втором случае не упоминают кроссбраузерность, а так и пишут IE8+
                                              • UFO just landed and posted this here
                                                  0
                                                  Ну это началось много лет назад. Ну и само слово говорит за себя.
                                                  Ваш вопрос звучит как «а почему красный шарик красный?»

                                                  На данный момент, под современные реалии я сделал пометку: «в край IE8+, но во втором случае не упоминают кроссбраузерность, а так и пишут IE8+». Или вы считает IE8 неактуальным? Так же вам выше упомянули о проблемах в Сафари.

                                                  Ну и замечательно, что остальную часть сообщения проигнорировали.
                                                  • UFO just landed and posted this here
                                                      –2
                                                      Ответил ниже

                                                      Ну да, из всех претензий в моем сообщение — до#$лись только до слова IE6, ну или пусть даже IE8, который лично вы не поддерживаете.

                                                      большое кол-во современных проектов не требуют поддержки IE8

                                                      На этот случай есть устоявшееся словосочетание «под актуальные браузеры» или «под современные браузеры».
                                                  0
                                                  К тому, что кроссбраузерность ныне понятие относительное. Требования поддержки IE старше 8 лично я не встречал уже год.
                                                    0
                                                    в край IE8+, но во втором случае не упоминают кроссбраузерность, а так и пишут IE8+
                                                      +1
                                                      Давайте поступим по другому, раз вам всем прям в глазу мозоль натерли три буквы IE6, а остальная часть моего поста игнорируется полностью.

                                                      Предположим, на фриланс бирже будет заказ «сверстать макет, кроссбраузерно». Попробуйте ответить себе честно — у вас к заказчику не возникнет вопроса: «кроссбраузерность с IE6 или IE8?» Только честно, подумайте. Уверен что возникнет. А отсюда делаем какой вывод? Подсказку дать, уважаемые технари, или сами проанализируете ситуацию?
                                                      • UFO just landed and posted this here
                                                          0
                                                          А что, проанализировать никак и сделать хоть какой-то вывод?
                                                          • UFO just landed and posted this here
                                                              0
                                                              Вы чертовски правы. Однако я всё равно пока не дам подсказку. Может кто ещё захочет пораскинуть мозгами.
                                                          +2
                                                          Если заказчик не уточнил, то я подразумеваю что речь идёт о актуальных версиях обозревателей. В случае с IE6 когда я имел дело однажды (больше 5 лет назад) заказчик сам сразу уточнил минимально поддерживаемую версию. Уже тогда ИЕ6 считался дико устаревшим и использовался только в корпоративной среде.
                                                          Так что мне кажется тут вы выдаёте желаемое за действительное.
                                                            0
                                                            Вот видите, вы, как jamepock выше, пишите ключевые слова «актуальных», «современных». В этом большая разница.

                                                            А про «больше 5 лет назад» вы всё лукавите:
                                                            Windows Internet Explorer 8 (IE8) вышел 19 марта 2009 года

                                                            И статистика ровно пять лет назад была такова (не российская, она всегда была раньше печальнее):
                                                            Третье место, можно сказать, разделили IE7 и IE8, набравшие 11.79% и 11.59% соответственно.
                                                            Далее идут Firefox 3.0 и Opera 9.6, набравшие в районе 7.5% процентов каждый.
                                                            И в качестве новогоднего подарка — доля IE6 составила всего 6.72%.

                                                            geektimes.ru/post/80073/

                                                            Давайте перефразирую ещё раз свой первый ответ в этой дискуссии — я не пытался сказать, что надо верстать под IE6, или даже IE8. И даже не говорил, что надо разрабатывать примеры под них. Но и привирать не надо, вранья маркетингового и так полно в нашей жизни. У автора явно пример «для современных браузеров», но ни как не кроссбраузерное решение.

                                                            А то что вы тут все докопались до упомянутых мной ишаков, это ровно тоже, что у автора упомянута всуе кроссбраузерность. Это больная мозоль, если вам так понятнее будет.
                                                            • UFO just landed and posted this here
                                                                0
                                                                Я всё правильно прочитал.
                                                                2015 — 5 = 2010

                                                                Windows Internet Explorer 8 (IE8) вышел 19 марта 2009 года


                                                                За год он набрал 11%. IE6 около 7% по забугорной статистики.

                                                                Итого более 5 лет назад IE8 только начинал появлятся. IE7 был не намного лучше IE6 и он обычно вообще в расчет не брался, шел на равне с IE6. Поэтому более 5 лет назад он не мог верстать отказатсья от IE6, как ни крути.
                                                                • UFO just landed and posted this here
                                                                    0
                                                                    Повторю, потому что «более пяти лет назад» не могло быть «однажды». Могу предположить, что у автора либо опыт незначительный, как минимум был, либо это не его сфера и он однажды случайно верстал.

                                                                    Можно даже на хабре поднять историю и посмотреть какие холивары были на тему «отказываться или нет от IE6», и как заглушки делали для этого браузера. Это было всё менее пяти лет назад.

                                                                    habrahabr.ru/search/?q=ie6
                                                                0
                                                                Читайте внимательнее, вот jamepock всё правильно понял.

                                                                Касаемо кроссбраузерности, насколько я знаю обычно понимают под этим поддержку самых известных обозревателей и о версиях речи не идёт. Типа IE, Chrome, FireFox, Safari (во многом поведение похоже на хромовское) и ранее Opera. Если версии не указаны значит речь про последние версии.
                                                                Кроссбраузерность не говорит что поддерживаются все обозреватели и тем более все их версии, как кроссплатформенность не говорит что поддерживаются все существующие платформы (а их гораздо больше чем известная тройка).
                                                                Тут конечно есть ошибка автора что он не указан на чём проверял, но вы тоже перегибаете тут палку с версиями IE.
                                                                  0
                                                                  Почитал и ответил. Можете тоже почитать теперь мой ответ.

                                                                  Касаемо кроссбраузерности, насколько я знаю обычно понимают под этим поддержку самых известных обозревателей и о версиях речи не идёт.

                                                                  Ну насколь вы знаете может быть. Я же говорю, как было ещё пару лет назад. Сейчас IE6 канул в лету, и это не может не радовать. Жизнь у IE8 тоже заканчивается. Однако слово кроссбраузерность не сильно поменяло значение. И оно не подразумевает полное соответствие современным браузерам (уже вики почти цитирую), но как минимум должно иметь обратную совместимость (graceful degradation или progressive enhancement). Об этом тут упоминали помимо меня.

                                                                  И именно поэтому и не только (есть и другие причины, у каждого свои) многие стараются избавиться от слов кроссбраузерная верстка и используют только в маркетинговых целях, ибо кроссбраузерная верстка, которая подразумевает как минимум IE8, как было замечено ранее, стоит дороже. Про наценку для требуется IE6 даже упоминать не стоит.

                                                                  Слово «кроссбраузерность» — было и, думаю, будет означать максимальное число поддерживаемых браузеров, а не только современных. А у автора проблемы явно не только с IE6-8.

                                                                  В общем я не вижу смысла продолжать холивар ни о чём.
                                                              0
                                                              До фриланс-бирж жизнь пока, к счастью, еще не довела, но я всегда утверждаю конкретный список, указывая стоимость работ для поддержки, в частности, каждой версии IE < 9 и Opera Presto. Ниже IE8 еще никто не хотел в последний год, для SPA с кучей джаваскрипта и от IE8 отказываются (ибо дороже).
                                                                0
                                                                Так я противоположное не утверждал, хотя я верстаю изначально так, что для ишака даже 6 хаков всего на 10-30 сторок CSS и поэтому цены не сильно увеличиваю. Разумеется я говорю про верстку сайтов, а не приложений. Что касается приложений или SPA — то конечно на JS, если не использовать либы типа jQ — это совсем другой разговор.

                                                                Однако, если рассмотреть ситуацию с биржей или вашим случаем — с клиентом, то в случае озвучивания в заказе «кроссбраузерности», вы будете озвучивать «стоимость работ для поддержки, в частности, каждой версии IE < 9 и Opera Presto», что как бы намекает на значение слова «кроссбраузерности».

                                                                Может для начинающих верстальщиков, с оптытом год-два кроссбраузерность это IE9+, но для людей с опытом более 5 лет — это куча сломаных мозгов на IE6 в своё время при «кроссбраузерном» решение.

                                                                И, господа хорошие, выше со мной спорившие, думаю можно ещё одну точку поставить на внимательность:
                                                                Кроссбраузерность — это работает везде, где работают label

                                                                Эти слова сказаны не мной!
                                                                  0
                                                                  Я не то, чтобы спорю. Моя мысль (подтвержденная практикой) — лишь в том, что каждый под словом «кроссбраузерность» сейчас уже все понимает что-то свое, и оно практически лишилось смысла. Проще версии браузеров перечислить.

                                                                  Да, в последнее время я делал в основном навороченные SPA, потому от одной мысли возиться с IE6 бросает в дрожь. :) Черт с ним, с CSS, хаки до сих пор наизусть помню. С JS все намного хуже, особенно когда используешь фреймворки, авторы которых поддерживать IE<8 не собираются, да и поддержку IE8 уже дропают.
                                                          +1
                                                          Похоже, вы пытаетесь доказать, что понятие «кроссбраузерность» должно включать в себя IE6. Но вроде как само слово «кроссбраузерность» это ни разу не «кроссбраузероверсионность» и вы определённо перегибаете.
                                                        0
                                                        Я так понимаю профессиональные верстальщики начали сливать без объяснений?
                                                          0
                                                          Ну я вот например в большей части ТЗ по сайтам встречал формулировку «кроссбраузерность с поддержкой IE8+». То есть, сама поддержка IE8 не считалась необходимой частью понятия кроссбраузерности. И это разумно — поскольку годы идут, и некоторые браузеры становится настолько невостребованными, что выпадают из термина «кроссбраузерности».
                                                          Мне кажется, в конкретных проектах вообще не имеет смысл использовать этот термин, поскольку, ну, вы им хвастаться что ли собрались? У нас кроссбраузерно сверстано, вуху! И все вокруг: «Ну офигеть теперь!»
                                                          Так что имеет смысл говорить о кроссбраузерности только в опенсорсных решениях / библиотеках. Но и в них — кроссбраузерность это достаточно размытое и субъективное понятие. Кто-то полезет смотреть в java-браузерах на старых нокиях, кто-то запустит IE6 под виртуалочкой. А кто-то прочекает в последних версиях хрома, фф и оперы и останется доволен. И опыт последних лет подсказывает, что прав будет только последний.
                                                        0
                                                        Мне кажется или на табуляцию оно не реагирует?
                                                          0
                                                          Мне статья понравилась, и хотя данное решение не может быть использовано в таком виде, сама идея весьма интересна.
                                                            0
                                                            А еще можно таблицы верстать DIVами, да

                                                            Only users with full accounts can post comments. Log in, please.