Трёхпозиционный checkbox (aka tristate) без скриптов и смс

  • Tutorial
Понадобилось мне недавно реализовать трёхпозиционный переключатель. Ну это такой, у которого вместо двух состояний «включено/выключено», есть ещё промежуточное состояние. Это часто используется, например, в чекбоксе «выбрать всё», для того чтобы показать, что выбраны не все элементы, а только часть. В общем, захотелось такое реализовать, да чтобы без скриптов.

Пример элемента на gmail:



Беглое гугление не выдало мне готового решения без JavaScript. Точнее, я нашёл парочку, но они предлагали для изменения состояния кликать в конкретное место на переключателе, что совсем неудобно. Хотя в основном предлагают использовать либо свойство DOM indeterminate у чекбокса, либо написание компонента на скриптах, начиная с jQuery, заканчивая Angular. Но мне как-то не хотелось ради такой простой задачи подключать JS вообще.

Ещё из пожеланий — мне хотелось иметь рабочий кликабельный label, а так как состояния три, то чтобы этот label показывал текущее состояние.

Как я это реализовал? В основе — обычные радио-инпуты спозиционированые абсолютно в верхний левый угол. А вся магия заключается в изменении z-index в зависимости от текущего выбранного элемента. Кусочек из CSS:
.tristate > input[type="radio"]:checked + input[type="radio"] {
  z-index: 10;
}

Иконка и текущий лейбл стилизуется подобными правилами:
.tristate > input[type="radio"]:checked + i + label,
.tristate > input[type="radio"]:checked + i + label + label {
  display: none;
}

.tristate-checkbox > input[type="radio"]:checked + input[type="radio"] + i {
  /* ... */
}

Осталось всё немного застилизовать, и получаем два симпатичных UI-компонента, как на картинке к посту:

Демо на CodePen

Работоспособность проверял в FF, Chrome, IE9+.

UPDATE: Добавил вариант с «крутилкой»:
Поделиться публикацией

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

    0

    Волшебно.


    Разве что тут ещё и scss не нужен имхо, можно было с тем же успехом воспользоваться чистым css (переменные, например) + пропозалами в него (nesting), но это уже совсем мелочи и вкусовщина, да и фиксится за минуту на месте.

      +2

      А, вот чего ещё — на тач-устройствах, для второго варианта, таскание его делает немного не то, чего может ожидать пользователь (перетаскивание влево из среднего состояния переключает вправо). Интересно, это можно на чистом css починить?

        0
        SCSS использовался только для удобства, всегда можно на codepen нажать «View compiled».
        На чистом CSS, конечно, свайпы не реализуешь)
          +1
          SCSS использовался только для удобства, всегда можно на codepen нажать «View compiled».

          Не, оно компилирует в унылый css, проще руками переписать c scss на css с нестингом и переменными, чем compiled упорядочивать. =)

            +3
            И сразу отмести IE? Пользователям нужен результат, а не красота в коде, увы
              0

              Так postcss же.

              0
              А что браузеры уже поддерживают scss? Или я что то не так понял?
                0
                Ещё раз scss — лишь для удобства написания, он компилируется в обычный православный CSS. На codepen можно посмотреть компилированный CSS.
                  –1

                  Переменные — поддерживают: http://caniuse.com/css-variables, но не все и не в таком виде. Спека — кандидат в рекомендацию, (как и flexbox, как и calc(), как и vw/vh, как и градиенты, как и многое другое). Полифиллится на стороне сервера через postcss.


                  Нестинг — не поддерживают, спека в черновике: http://tabatkins.github.io/specs/css-nesting/, но она от того же человека из гугла что и переменные выше. У него есть все шансы. Полифиллится на стороне сервера через postcss.


                  Кроме этого, в цсс довольно много других вкусных штук ожидается (полифиллы, опять же, есть).


                  scss не поддерживают, но разумные альтернативы его фичам — да.

                    0
                    Понятно, спасибо большое.
                      0
                      У переменных спека «Editor’s Draft, 19 August 2016».
                      А как их полифилят? Они же в браузере на лету могут значения менять.
                      Как на postCSS такое поведение реализовать https://googlechrome.github.io/samples/css-custom-properties/?
                        0
                        Таки версия 2015 в RC
                          0
                          У переменных спека «Editor’s Draft, 19 August 2016».
                          Таки версия 2015 в RC

                          Последняя версия спеки (в данном случае https://drafts.csswg.org/css-variables/) — зачастую Editor’s Draft, и это та самая версия, на которую надо ориентироваться. Там обычно учтены последние замечания. Считайте это «master»-ом =).


                          Но у этих черновиков в заголовке есть ссылки на последние опубликованные версии, в данном случае это:


                          Latest published version: https://www.w3.org/TR/css-variables-1/

                          Которая и является Candidate Recommendation.


                          Это справедливо и для других W3С спек: последняя («Editor’s Draft») — основная и самая интересная, а статус смотреть по «Latest published version». Ну, кроме тех, что уже зарелизили, конечно.

                          0

                          Согласен, без динамики и скриптов в клиенте — никак.


                          Под полифиллами я имел ввиду, что оно работает на таком же уровне, как и переменные в scss, о котором был разговор выше. Ответ был в том, что браузеры scss не поддерживают, а вот его фичи — или поддерживают, или планируют, и полифиллятся эти фичи временно на стороне сервера через postcss — то есть получается ничуть не хуже, чем postcss.


                          Так-то новые возможности css могут давать гораздо больше, чем то, что можно в статике сполифиллить. Те же vh/vw без js не сполифиллить.

              0
              Стоит заметить, что это не совсем чекбокс. Этот компонент не получится использовать также, как в Gmail.
              Это скорее мультипозиционный переключатель — что в общем то тоже клевая штука.
              Если его еще оформить в виде крутящейся ручки с рисками, то будет еще круче.
              Кстати, я правильно понимаю, что мы не ограничены тремя радиокнопками? И что мы можем сделать 4-х и более позиционные переключатели?
                0
                Да, можно сколько угодно наклепать состояний при желании.
                  0
                  И кстати можно спокойно застилить в виде крутящихся ручек. Это уже вопрос стилизации.
                  На самом деле по функционалу это всё тот же набор радио-кнопок, просто застилизованный в виде одного элемента.
                    0
                    Добавил вариант с поворачивающими ручками:
                    image
                    0
                    Интересное решение, спасибо.)
                      0

                      Чекбокс выглядит здорово!
                      Но если устраивает поддержка IE10+, то использовать input type="range" для второго варианта (ползунка) куда проще и удобнее. Пальцем опять же потягать можно.

                        0
                        Тогда получится наоборот — двигать можно, а щёлкнуть нельзя.
                          0
                          И как его стилизовать?
                            0

                            Для стилизации даже конструктор CSS есть:
                            http://danielstern.ca/range.css


                            Обработка клика реализуется парой строк JS. Но, мне кажется, что таскать его куда интуитивнее чем кликать.

                              0
                              Парой строк js можно и свайп реализовать) Глянул я как это стилизуется — кошмар, сплошные вендорные псевдоэлементы. В общем, главное в моём посте — показать как можно застилить набор radio-инпутов, а применять или нет — это уже каждый сам решает.
                        0
                        Прикольно, конечно, запилить всё это на css. Типа ради спорта. На ангуляре будет чуть меньше разметки, и на порядок меньше CSSa. Правда есть микродоза JS (таки пришлось объявить приложение и пустой контроллер)
                        Пример тут (добавил снизу).
                        // Встречный ангулар-прикол: классы для вращения не писал, а прямо в разметке забиндил формулы в атрибут style.
                          0
                          Ну я бы не сказал что у вас разметка сильно проще, и CSS много меньше. Сейчас в примере много CSS так как три варианта переключателей реализовано. А так на JS, конечно можно что-то «одной строчкой» сделать, только предварительно несколько десятков килобайт скриптов подключить)
                          0
                          Аналогично реализовывал переключение форм регистрации/входа. Честно говоря, думал, что эта практика известна =)
                            0
                            Идея не сложная, только почему то для такого элемента не распространена.
                              +1
                              Довольно много кода css вместо нескольких строк на js думаю оказывают влияение на распространение.

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

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