Простая кастомизация Checkbox и Radio

    Вместо вступления


    Всем доброго времени суток!

    Кастомизация элементов форм наверное одно из самых увлекательных занятий в Веб-дизайне. Оправданное ли это занятие решает каждый для себя сам, хотя очевидно, что в наше время дизайнеры и верстальщики все же уделяют этому свои силы и время.

    К сожалению, одного только CSS частенько не достаточно, чтобы с легкостью оформить тот или иной элемент формы. Наверное поэтому многие используют штуки вроде Uniform для своих форм. Лично я всегда стараюсь сократить количество Javascript, используемого в подобных целях, поэтому хочу рассказать о совершенно нативном HTML+CSS методе кастомизации радио-кнопок и чекбоксов.

    Уверен, что на большая часть специалистов на Хабре, применяют схожие методы, для тех же, кто использует js-библиотеки, вроде Uniform, надеюсь будет полезной эта статья.

    Поехали!


    Итак, сразу поставим цель: оформить чекбоксы и радио-кнопки, чтобы внешне они были похожи на js-магию Uniform, но сделать это максимально простым, нативным по отношению в HTML и CSS способом, а также избежать применения лишних тегов, сохраняя семантику. Как-то так.

    Основная идея строится на нативном «умении» HTML тега label устанавливать связь с определенным элементом формы. Собственно и все, дальше только код.

    Разметка

    <ul>                
        <li>
            <input id="cfirst" type="checkbox" name="first" checked hidden />
            <label for="cfirst">Checked checkbox</label>
        </li>
        <li>
            <input id="csecond" type="checkbox" name="second" hidden />
            <label for="csecond">Unchecked checkbox</label>
        </li>
        <li>
            <input id="cthird" type="checkbox" name="third" hidden disabled />
            <label for="cthird">Disabled checkbox</label>
        </li>
        <li>
            <input id="clast" type="checkbox" name="last" checked hidden disabled />
            <label for="clast">Disabled checked checkbox</label>
        </li>
    </ul>
    <ul>                
        <li>
            <input id="rfirst" type="radio" name="radio" checked hidden />
            <label for="rfirst">Checked radio</label>
        </li>
        <li>
            <input id="rsecond" type="radio" name="radio" hidden />
            <label for="rsecond">Unchecked radio</label>
        </li>
        <li>
            <input id="rthird" type="radio" name="radio" hidden disabled />
            <label for="rthird">Disabled radio</label>
        </li>
    </ul>
     

    Совершенно нативная разметка. Использование label вместе с input прям как из учебников. Важным моментом является только то, что нужно указывать id для каждого input и for для label, чтобы связать их.

    Думаю все заметили использование атрибута hidden, который скрывает сами input элементы, однако благодаря связи с label, мы все еще может манипулировать ими. В результате мы получаем что-то вроде:


    Скучно совсем стало, но все работает. Теперь осталось оформить все это добро как надо. Для этого воспользуемся спрайтом, который применяется на сайте Uniform.

    Оформление

    input[type="checkbox"],
    input[type="radio"] {
        display:none;            
    }
    input[type="checkbox"] + label, 
    input[type="radio"] + label {
      font: 18px bold;
      color: #444;
      cursor: pointer;
    }
    input[type="checkbox"] + label::before,
    input[type="radio"] + label::before {
        content: "";
        display: inline-block;
        height: 18px;
        width: 18px;
        margin: 0 5px 0 0;
        background-image: url(uniformjs.com/images/sprite.png);
        background-repeat: no-repeat;
    }
    input[type="checkbox"] + label::before {
        background-position: -38px -260px;    
    }
    input[type="radio"] + label::before {
        background-position: 0px -279px;
    }
    input[type="checkbox"]:checked + label::before {
        background-position: -114px -260px;
    }
    input[type="radio"]:checked + label::before {
        background-position: -108px -279px;
    }
    input[type="checkbox"]:disabled + label::before {
        background-position: -152px -260px;
    }
    input[type="checkbox"]:checked:disabled + label::before {
        background-position: -171px -260px;
    }
    input[type="radio"]:disabled + label::before {
        background-position: -144px -279px;
    }
    input[type="radio"]:checked:disabled + label::before {
        background-position: -162px -279px;
    }

    Тут все так же максимально просто. Используем псевдо-элемент before для того, чтобы показывать наши «виртуальные контролы» и пользователь не заметил подмены. Части спрайта, меняем в зависимости от состояния input'а.

    В результате получаем что-то вроде:



    Демо

    Выгода использования подобного подхода по сравнению с тем же Uniform очевидна. Никаких javascript для оформления, никаких лишних тегов, более простая, правильная и семантичная разметка. Такой же способ можно использовать для придания данным элементам более причудливых форм. К примеру, без проблем можно заделать чекбоксы в стиле iPhone не применяя при этом javascript.

    Надеюсь для статья будет полезна начинающим верстальщикам и остановит их о использования js-костылей для подобных целей. Спасибо за внимание!

    UPD: Как было отмечено уважаемым SelenIT2 данный способ вероятно не будет работать в браузере Safari под платформу iOS из-за досадной ошибки в поддержке html спецификации (ссылка).
    Поделиться публикацией

    Комментарии 77

      0
      кастумизация — нет такого слова
        0
        Лично для меня смысл совершенно понятен. Спасибо за статью.
          –3
          если уж и использовать такие слова, то хотябы писать их без ошибок — кастОмизация
            –3
            Ну извините, если это задело ваши чувства. Просто в свое время я так привык произносить. На полезность или бесполезность статьи это никак не влияет. В любом случае исправил.
            +3
            Карова доёт харошее малако — тоже не лишено смысла, но ведь вы не будете так писать
              +3
              не согласен. это ошибка в русском слове. а кастомизация — это больше жаргон. меня тоже немного напрягло, но не настолько чтобы троллить на эту тему.
                0
                Вас и многих других следует перевести в рид-онли за флуд. Я поражен: человек написал хорошую, понятную статью, а вы обсуждаете правило написания слова. Отпишите в личку об ошибках, человек исправит. И это будет очень хорошо с вашей стороны, что вы так поступили. Но не в комментах, это не грамота.ру.
            +7
            Смесь английского с нижегородским, почти по Грибоедову :)
              +3
              Тогда уж костюмизация — одевание новой шкурки (костюмов) на чекбоксы, радиобатоны.
              • НЛО прилетело и опубликовало эту надпись здесь
              –7
              > кастумизация — нет такого слова

              На мой взгляд это вполне прижившая в среде разработчиков «русификация» от англ. customization. Правильный перевод этого термина имеет достаточно большую длину, чтобы использовать его. К тому же итак понятно что имеется ввиду.
                +8
                кастомизация. кастомайзинг. и не только у разработчиков, но везде. Но никак не кастумизация.
                  –3
                  «эджекс» — а не «аякс», как как произносят многие российкие разработчики. «майэскюэль», вместо «майсиквел» как произносит один мой знакомый очень уважаемый разработчик с 25 летним стажем. что это меняет то? мне теперь ему на это указывать каждый раз? привычка.
                    +4
                    А с каких пор буква s начала читаться как си? MySQL правильно произносить как «майэскьюэл».
                      +2
                      Если бы вы работали также долго как он, то знали бы почему многие «деды» произносят именно так. В любом случае, читайте историю.
                        0
                        А я поправил не его, а вас )
                          0
                          Вы задали вопрос, я ответил.
                            +1
                            А по теме, статья понравилась, добавил в избранное на будущее.
                              +1
                              Спасибо.
                  +3
                  Семантически верный перевод: стилизация. Длина этого слова меньше используемого автором.
                  (меня тоже дико раздражают всякие кустомизации, коворкинги и краудфандинги)
                  0
                  А opacity:0 + спрайт не работает?
                    +1
                    Кому как нравится. Лично я использую этот способ. Для меня он удобнее.
                    0
                    Скрытые инпуты работаю в ИЕ?
                      +1
                      само свойство hidden к сожалению нет, поэтому в css еще добавлено display: none; для данных типов input.
                        +1
                        Тут помимо этого присутствует псевдоэлемент :before, который не работает в IE7.
                        Для него придется использовать expression, либо добавлять какой-нибудь пустой тег вместо :before.

                        Ну, и еще IE7 не поддерживает :checked и :disabled, тоже придется писать костыль…
                        • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            IE8 тоже не поддерживает :checked, :enabled и :disabled, а статистика говорит, что его доля среди российских юзеров немного больше, чем у IE9
                            • НЛО прилетело и опубликовало эту надпись здесь
                                0
                                Думаю, да, другого варианта здесь нет :)
                                Только если заказчик будет сильно упираться, чтобы и в нем все выглядело красиво, то придется выкручиваться…
                                • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Ок, а как вы текст будете менять в label без JS в зависимости от состояния чекбокса?
                          0
                          А в каком случае его нужно менять? В стандартной реализации текст в лейбле тоже не меняется.
                            0
                            Ах да, верно. Меня смутили захадкоденые надписи в лэйбле вроде «checked», «disabled», итд.
                            +1
                            В данном конкретном случае, мне это не нужно. Если вы имеете ввиду чекбоксы в стиле iPhone, то там тоже нет каких-то существенных проблем.
                            • НЛО прилетело и опубликовало эту надпись здесь
                              –4
                              Ваш способ прекрасен до того момента, как вы попробуете на ваши инпуты нажать.

                              Данная реализация не работоспособна. Вот это её убивает:

                              input[type="checkbox"],
                              input[type="radio"] {
                              display:none;
                              }
                                0
                                А что в этом не так? Элементы формы не будут отсылаться при отправке на submit? Если так, то способов как их скрыть кроме display:none множество.
                                  +1
                                  Все работает совершенно корректно. Такие input'ы не только нажимаются, но и без проблем отправляются в формах.
                                    –5
                                    Виноват, не заметил атрибута «hidden». Без него — всё плохо.
                                  0
                                  Ещё один минус текущей реализации:

                                  Вы расчитываете на то, что сначала в HTML идёт input, а затем — label. Очень часто — наоборот. Не могли бы показать модификацию для такого случая?
                                    0
                                    А еще нередко бывает, когда input и label не идут в потоке подряд. Например, находятся в разных ячейках таблицы.
                                      +2
                                      Я рассматриваю конкретный случай, взяв для этого классическую разметку как «по учебнику». Целью было показать идею, одну из многих, которая может для кого-то оказаться удобной. Для каждого случая свои инструменты. В случае если label идет впереди input возможно лучше использовать другой способ.
                                        +3
                                        бессмысленно класть input в какое-то отдельное от label место, если он display: none. проблема высосана из пальца.
                                        +4
                                        Важным моментом является только то, что нужно указывать id для каждого input и for для label, чтобы связать их.

                                        Можно ведь тег input поместить внутри контейнера label и таким образом избавимся от написания
                                        id для каждого input и for для label
                                          0
                                          Да, для radio и checkbox очень часто используют именно такую конструкцию. Но мне кажется, что в этом случае внедрить подход, который предлагает автор, на чистом CSS будет весьма сложно.
                                            +2
                                            Как-то так: label > input::before
                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                +1
                                                Не проверял, не знаю. Но даже если и не сработает, то можно рядом с инпутом поместь пустой элемент, например <i/>, и в нем отображать нужный флажок.

                                                В CSS-е будет что-то вроде label > input:checked + i { display:inline-block; }
                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                            –1
                                            А если я кликну на зону лейбла, где инпут не дотягивается, разве произойдёт ивент?
                                              +1
                                              Что значит «инпут не дотягивается»? Событие вызывается по клику на самом лейбле.
                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                  0
                                                  Вообще возможность связать label и input предусмотрена самим HTML и для checkbox и radio считается хорошим тоном. Автор просто предложил вариант использования этой возможности для кастомизации этих контролов.
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                      +1
                                                      Спасибо за полезную информацию.
                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                  0
                                                  Точно, забыл про всплытие.
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                      0
                                                      Просто, честно сказать, в первый раз узнал о конструкции инпута внутри лейбла.
                                                        0
                                                        Да говорят же Вам: это работает независимо от помещения инпута внутри лейбла (если только они связаны через id/for), это не всплытие.
                                                          0
                                                          Я понял, спасибо, просто прочитал так, как будто если в лейбл всунуть инпут без id/for. Ещё раз извините за глупость. )
                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                  0
                                                  Селектора по потомку в актуальном CSS

                                                  Я полагаю, по причине производительности. Честно говоря, диву даюсь, как браузеры существующие спецификации умудряются поддерживать без тормозов.
                                                +1
                                                Мда, в статье про «Простую кастомизация Checkbox и Radio» результат оформлен картинкой :)
                                                  0
                                                  А чем по-вашему он должен быть оформлен?
                                                    +1
                                                    Добавил ссылку на демо
                                                      0
                                                      Да у меня, собственно, претензий-то и не было особых :) Просто, дойдя в тексте до картинки, попытался пощелкать по чекбоксам. Не получилось :)
                                                    0
                                                    Работал с каким-то плагином аналогичного назначения, возникла проблема из bind на разные события. Плагин сделали, кнопки нажимаются, все счастливы, вот только не учли, что помимо картинки оно должно еще и работать.
                                                      +1
                                                      Не уловил суть. В статье речь идет как раз об отсутствии необходимости использования каких-либо сторонних костылей, в виде js-плагинов в их числе.
                                                        0
                                                        И куда я смотрел… Извините, ошибся, ваш метод действительно много надежней.
                                                      0
                                                      А сами спрайты, отображающие состояние, кликабельны ведь, да? Я просто не сильно клиентский программист, поэтому интересуюсь. А рабочего примера не выложили, как раз было бы очень кстати, я бы тогда не задавал таких глупых вопросов :)
                                                        +2
                                                        Да, все работает вполне нативно. Можете проверить: демо
                                                        0
                                                        Супер. Ещё бы чтобы от клавиатуры работало, было бы здорово.
                                                          0
                                                          Ваш вариант кастомизации не очень удобен, в нём невозможно переходить по инпутам нажатием [Tab]. Здесь я нашел более подходящий для себя вариант.
                                                            0

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

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