Красивые чекбоксы и радиокнопки на CSS3 без JavaScript

  • Tutorial
Благодаря псевдоклассу :checked, появившемуся в CSS3, можно стилизовать формы с чекбоксами и радиокнопками как угодно. В этом топике рассмотрен один очень простой способ, причем без использования JavaScript.



Демонстрация Скачать исходники

Для начала сделаем простой checkbox:

<input type="checkbox" id="c1" name="cc" />
<label for="c1"><span></span>Check Box 1</label>


Теперь необходимо спрятать чекбокс и использовать спрайты для отображения отмеченного чекбокса/радиокнопки:



input[type="checkbox"] {
    display:none;
}
input[type="checkbox"] + label span {
    display:inline-block;
    width:19px;
    height:19px;
    margin:-1px 4px 0 0;
    vertical-align:middle;
    background:url(check_radio_sheet.png) left top no-repeat;
    cursor:pointer;
}


Осталось только заставить все это работать. По клику должен меняться спрайт с отмеченного на неотмеченный и наоборот:

input[type="checkbox"] {
    display:none;
}
input[type="checkbox"] + label span {
    display:inline-block;
    width:19px;
    height:19px;
    margin:-1px 4px 0 0;
    vertical-align:middle;
    background:url(check_radio_sheet.png) left top no-repeat;
    cursor:pointer;
}
input[type="checkbox"]:checked + label span {
    background:url(check_radio_sheet.png) -19px top no-repeat;
}




Поддержка браузерами


Псевдоклассы, в частности используемый :checked, отлично работают в большинстве браузеров, за исключением Internet Explorer 9 (и ниже) и Safari в iOS ниже 6-ой версии. Вот так наша форма отображается в IE:



Пост написан по мотивам урока на tutplus.com Quick Tip: Easy CSS3 Checkboxes and Radio Buttons.
Поделиться публикацией
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 89
    +2
    Я думаю, ничто не мешает для IE сделать JavaScript-хак.
    –4
    Осталось ещё для input file и select. А что там с кроссбраузерностью и с какой версии браузеров работает?
      0
      С file все проще: jsfiddle.net/tsLyW/ и будет работать во всех браузерах. Есть еще мысль заменить span на :before, но в любом случае понадобится контейнер.

      А с select вариантов стилизации не особо много. Меня лично бесят кастомные селект-боксы. В большинстве своем они выглядят куда более отвратительно нежели нативные.
      • НЛО прилетело и опубликовало эту надпись здесь
          0
          Только вот JS уже используется. Но да, так проще.
          0
          Я с селектами поступаю так же, как и с file — при opacity:0; сам селект скрывается, а вот список имеет 100% видимость. Правда, чтобы в стилизованном спане показывать выбранный пункт, приходится все-таки использовать скрипт, но там буквально пара строк.
            0
            В IE 9 у меня только по двойному клику окно открывается
              0
              Нет поля, где бы отображалось название выбранного файла. Непонятно, выбран уже файл (и какой) или нет.
                0
                В лисе не вся область кликабельна.
              +1
              0 С дополнительной разметкой
              1 Сам элемент убран, поэтому без метки ничего работать не будет.

              С дополнительной разметкой я и сам могу. Элемент надо располагать после инпута, инпут делать прозрачным, потом отрицательный отступ и z-index. Клики ловятся инпутом, а элемент отвечает за отображение.
                +9
                Всё хорошо, но вместо лишнего SPAN ведь можно использовать ::before?
                  0
                  А как скроете оригинальный контрол?
                    0
                    ::before относится к <label>, причём тут контрол?
                      0
                      Как вы будете ловить нажатие на контрол? Получится, что клик по тексту label будет работать, а по самой картинке (т. е. псевдоэлементу) — нет.
                        0
                        Щито? Всегда работало.
                          0
                          Можете показать пример? У меня не работает нажатие на псевдо-элемент, когда вешаю его на label.
                            +2
                            Заменил span на ::before: jsfiddle.net/wwnAg/. Проблем не наблюдаю: IE9, Chrome, Firefox, Opera.
                              –2
                              Остаётся вопрос, что делать, если инпуты находятся в отдельном столбце таблицы, подразумевая, что надо выбрать/отметить её строку, а роль общего label будет выполнять th соответствующего столбца.

                              Или любой другой вариант на выбор.
                                0
                                Использовать для оформления что-то рядом с чекбоксами, очевидно.
                                  –1
                                  А клик чем ловить?
                                    0
                                    Используйте label? Ну, подумайте сами уж.
                                      –1
                                      Пустой label? Или label в другой ячейке таблицы?
                                      Эта штука будет круче Фауста Гёте.
                                      Клин надо ловить самим инпутом с opacity:0, а визуализирующий элемент добавлять после инпута.
                                      Выше по тексту я описал решение.
                                      Работает фокус, работает active, работает disabled, работает клавиатура.
                                        0
                                        Зачем тогда Ваньку валяете?
                                          0
                                          Критика должна быть конструктивной.
                                          А то некоторые любят «это просто ещё один метод» и «все же с чего-то начинали».
                                            0
                                            Что-то вы не по адресу пишите, видимо.
                                              0
                                              Автора вообще не видать, видимо, ему плевать на читателей.
                                                0
                                                Оригинального мы вряд ли увидим, это ж перевод. А переводчику, очевидно, обсуждение не интересно.
                                +2
                                А теперь уберите оттуда:

                                input[type="checkbox"] + label {
                                    color:#f2f2f2;
                                    font-family:Arial, sans-serif;
                                    font-size:14px;
                                }
                                


                                и удивитесь))
                                  0
                                  Зачем? Если у вас браузеры бажат, что не делают перерисовку без изменений соответствующего элемента, то это надо сообщать о багах их разработчикам.
                                    0
                                    Это из-за font-size, проверил в хроме и сафари на макоси — не работает, если убрать это свойство. Нет возможности сейчас в других браузерах посмотреть, интересно, как поведут себя не-webkit.
                                      0
                                      Похоже на баг в Webkit, в остальных браузерах работает.
                    +5
                    В IE работает. Проверьте.
                      0
                      Да, в 9 действительно работает.
                        0
                        Просто из-за комментария перед доктайпом страница открывается в режиме совместимости.
                          +1
                          И это кстати фейл, потому что в IE 7-8 в режиме стандартов контролы показываются, но не работают. Надо делать деградацию или дописывать JS (что не отменяет деградацию в случае без JS).
                            0
                            сделал деградацию добавлением body:not(#foo) вначало так

                            body:not(#foo) input[type=«checkbox»] {
                            display:none;
                            }

                            body:not(#foo) input[type=«checkbox»] + label span {
                              +1
                              Да деградаций много можно придумать, одна из самых простых с использованием CSS Namespaces:
                              *|input[type=«checkbox»] {
                                  display:none;
                              }
                              
                              *|input[type=«checkbox»] + label span {
                              ...
                              
                            +1
                            Уточнение: это так на оригинальном примере.
                          +3
                          И ещё вопрос, если вы спрятали инпут, каким образом его показывать в старых браузерах?
                            0
                            А разве так не получится?

                            Вместо:
                            input[type=«checkbox»]:checked

                            написать:
                            input[checked=«checked»]
                              0
                              Получится, но работать не будет. Потому что селектор по атрибуту берёт значение из загруженного HTML. Иначе зачем бы потребовался псевдо-класс :checked?
                                –1
                                Нет, т.к. при изменении этого параметра, сам аттрибут не измениться, тут в css нужен именно псевдокласс.
                                +8
                                Нужно, чтобы можно было табуляцией фокус устанавливать.
                                  +9
                                  Ещё одна публикация на тему, как пользоваться CSS3 селекторами, которым три года уже. Причём оформление не на CSS3, а на картинках, которые будут замыленны на ретине, и даже SVG не используется. Несвежо как-то.
                                    +3
                                    А что мешает подложить рядышком check_radio_sheet@2x.png для ретины?
                                      0
                                      Кроме ретины есть ещё масштабирование, скажем. Как этот файл поможет вам при масштабе 150% в десктопном браузере?
                                        0
                                        А что, нативные чекбоксы как-то себя ведут при масштабировании в десктопном браузере? Проверил в вебкитах, они не масштабируются.
                                          0
                                          Вот как раз Webkit на десктопах плохо масштабируется. С этим дела лучше у IE и Fx.
                                            0
                                            Вот например Fx при 150% (при 150% dpi в системе):
                                            Скрытый текст

                                            В IE 9 похуже, там контролы не меняют размера.
                                              0
                                              Задайте им размер, тогда отмасштабируются.
                                        0
                                        Псевдоклассы, в частности используемый :checked, отлично работают в большинстве браузеров, за исключением Internet Explorer 9 (и ниже)

                                        Уверены, что в IE9 не работает :checked?
                                          0
                                          Работает, вот с плюсом могут быть баги.
                                          0
                                          Надо же как можно, оказывается:

                                          input + label span
                                            +13
                                            Опять управление с клавиатуры убито напрочь.
                                              +1
                                              В Safari в iOS :checked работает, там не работает привязанный label.
                                              Чтобы он заработал достаточно прописать ему пустой атрибут onclick.

                                              <input type="checkbox" id="c1" name="cc" />
                                              <label for="c1" onclick=""><span></span>Check Box 1</label>
                                              
                                                +1
                                                Ну или добавить cursor:pointer. Это «фича» iOS.
                                                0
                                                В общем решение в посте так себе. Учитывая баги Webkit, которые обнаружены выше, я бы сделал примерно так (если опустить красивости):

                                                <input type="checkbox" id="c1" name="cc">
                                                <label for="c1">Check Box</label>
                                                

                                                input[type="checkbox"] {
                                                  display: none;
                                                }
                                                
                                                input[type="checkbox"] + label {
                                                  font-size: 14px; /* для webkit */
                                                  cursor: pointer;
                                                }
                                                
                                                input[type="checkbox"] + label::before {
                                                  display: inline-block;
                                                  width: 19px;
                                                  height: 19px;
                                                  background: url('check_radio_sheet.png') left top no-repeat;
                                                  content: "";
                                                  vertical-align: middle;
                                                }
                                                
                                                input[type="checkbox"]:checked + label::before {
                                                  background-position: -19px top;
                                                }
                                                
                                                  0
                                                  Имхо, это вообще использование :checked не по назначению. Да, можно провернуть такой трюк, но псевдокласс всё же создавался для других целей. К тому же таким образом нарушается внешний вид привычных пользователю контролов. Впрочем в Chrome есть и другой баг: фон чекбокса под Windows (в других ОС не проверял) не меняет цвет, оставаясь белым :-(.
                                                    0
                                                    Ну, в продакшн я бы вообще так не делал)
                                                    Имел ввиду конкретно этот пост.
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                        0
                                                        Например, делать чёрный текст для выделенной опции и серый для невыделенной. Ну или ещё как-то улучшать вид форм.
                                                    0
                                                    Очень жаль, что по спецификации нельзя применять псевдоэлементы ::before и ::after к инпутам. Только Вебкит нарушает спецификацию.
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                      0
                                                      Круто
                                                        +2
                                                        Из комментариев узнал больше чем из статьи
                                                          +4
                                                          Сейчас меня «накроет морем минусов» и всего такое, не важно, но сколько еще можно постить на хабру такую «воду»?

                                                          Хотите написать статью, изучите тему вдоль и поперек, то, что другие не знают, какие-то мелочи, детали, оставшиеся за сценой, что за унылое говно, ну, почему так, а?
                                                            –2
                                                            В ключе данной статьи «изучить вдоль и поперек» это слишком громко. Способов это сделать на самом деле масса. Не стоит быть настолько уж суровым, бывают статьи и похуже, и скушнее, и водянистее… Причем в разы.

                                                            Хотя я бы не отказался от статейки по обзору различных способов стилизации форм.
                                                              +5
                                                              Написать на изображении «No JavaScript» это совсем не комильфо. Если уж что-то и делать, то с обратной совместимостью и с учетом graceful degradation.

                                                              Есть такие (глубоко нативные, привычные и необходимые) вещи, которые никто не отменял, например Tab или Shift + Tab. Это должно поддерживаться. Ровно, как и выбрать чекбокс тем же «пробелом».

                                                              Фокуса на элементе не видно, без мыши — ноль копеек цена такой форме с такими элементами. Да что это я в самом деле…
                                                                0
                                                                Без яваскрипта не получится, надо же как-то проверять доступность. а Модернайзер — это как раз яваскрипт.
                                                                А если с яваскриптом, то уже всё равно, чем вставлять доп разметку. Даже лучше на клиенте, не надо лопатить шаблонизаторы и переписывать все формы.

                                                                Написанное один раз, работает везде.

                                                                Самому, что ли, статью написать.
                                                                  +3
                                                                  Я о том и пишу, смысл это статьи какой?

                                                                  Ее нужно было назвать как,
                                                                  «Охуенные чекбоксы, которые как-бы не везде работают и не совсем правильно работают. Но охуенные.».
                                                            0
                                                            Мне больше нравится вариант без использования label. Как-то так jsfiddle.net/9AFKW/8/
                                                              +1
                                                              Хороший пример, но обёртка не нужна, можно обойтись отрицательным отступом для позиционирования замещающего элемента.
                                                                0
                                                                Можно, но я предпочитаю такие вещи в цельный блок объединять.
                                                              +1
                                                              Статья хорошая, спасибо. Поставил Like

                                                              Но меня всегда печалила необходимость придумывать и указывать идентификатор для атрибута «for», конечно, если форма собирается шаблонизатором, то никаких проблем нету, но для production ваше решение тоже не подходит, хотя бы потому, что не работает управление с клавиатуры.
                                                              Я лично предпочту маленький js-файл (~700 байт) для отказа от атрибута «for» и для работающего управления с клавиатуры
                                                              Пример

                                                              А вообще, в CSS4 Selector API предусмотрен Reference Combinator и Parent Selector для таких случаев. Возможно, в будущем мы сможем написать:
                                                              label! /for/ input:checked {
                                                                  box-shadow: yellow 0 0 10px;  
                                                              }  
                                                              

                                                              Правило применится к элементу <label>

                                                              Жду не дождусь статей «Красивые чекбоксы и радиокнопки на CSS4 без JavaScript» :)
                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                  0
                                                                  Почему же огорчать? CSS такой же живой стандарт, как и HTML. CSS4 всего лишь сокращение для CSS Selector API level 4 и он таки будет, просто под именем CSS. Но я вас уверяю, когда браузеры начнут внедрять CSS Selector API level 4, они в своих пресс-релизах будут использовать именно термин CSS4, как было с HTML5.
                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                  +1
                                                                  Смотрите мой комментарий выше.
                                                                  Добавил ещё выделение при фокусе jsfiddle.net/9AFKW/30/
                                                                    +2
                                                                    Это круто, но, для меня лично, семантически некрасиво. Да и добавьте теперь сюда текст для инпута, да чтобы он ещё был связан с инпутом: клик по тексту -> клик по инпуту

                                                                    Что уж говорить, я даже twitter bootstrap отредактировал, чтобы он поддерживал <input> вложенный в <label>
                                                                +1
                                                                Где-то я эту идею уже видел. Ах да, в моей статье.
                                                                habrahabr.ru/post/150760/
                                                                  +3
                                                                  Сила визуализации налицо.
                                                                    0
                                                                    Да, пожалуй, нужно больше картинок.
                                                                  +1
                                                                  использую этот способ уже второй год, докопался до него через анализ родных чекбоксов в Google Chrome.
                                                                    0
                                                                    а, знаете, вру — в способе моём Google Chrome в сам label подставляется непосрдственно битмэп, через псевдокласс :before — таким образом, те же яйца, только без спана.
                                                                    +2
                                                                    Очень круто, спасибо)
                                                                      +2
                                                                      у этого способа есть еще один серьезный недостаток.

                                                                      Нередко случается, что текстовый checkable-контент превышает по своему объему 1 строку (либо съезжает на вторую при увеличенном масштабе системных шрифтов, что не редкость у юзеров маков). В этом случае начало второй строки находится непосредственно под чекбоксом.



                                                                      Мало того, при высоте битмэпа большей, чем line-height, прыгнет межстрочник.
                                                                        0
                                                                        Описывал подобный способ еще год назад вот здесь.

                                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                        Самое читаемое