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

Пример элемента на 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)


  1. ChALkeRx
    18.08.2016 13:46

    Волшебно.


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


    1. ChALkeRx
      18.08.2016 13:51
      +2

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


      1. PaulZi
        18.08.2016 13:54

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


        1. ChALkeRx
          18.08.2016 15:34
          +1

          SCSS использовался только для удобства, всегда можно на codepen нажать «View compiled».

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


          1. delfi
            18.08.2016 17:12
            +3

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


            1. ChALkeRx
              18.08.2016 21:41

              Так postcss же.


          1. webdev_proxa
            18.08.2016 18:56

            А что браузеры уже поддерживают scss? Или я что то не так понял?


            1. PaulZi
              18.08.2016 18:58

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


            1. ChALkeRx
              18.08.2016 21:46
              -1

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


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


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


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


              1. webdev_proxa
                18.08.2016 22:04

                Понятно, спасибо большое.


              1. ImKremen
                19.08.2016 13:39

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


                1. ImKremen
                  19.08.2016 13:43

                  Таки версия 2015 в RC


                  1. ChALkeRx
                    19.08.2016 15:33

                    У переменных спека «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». Ну, кроме тех, что уже зарелизили, конечно.


                1. ChALkeRx
                  19.08.2016 15:38

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


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


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


  1. mggtsnppr
    18.08.2016 15:00

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


    1. PaulZi
      18.08.2016 15:22

      Да, можно сколько угодно наклепать состояний при желании.


    1. PaulZi
      18.08.2016 15:33

      И кстати можно спокойно застилить в виде крутящихся ручек. Это уже вопрос стилизации.
      На самом деле по функционалу это всё тот же набор радио-кнопок, просто застилизованный в виде одного элемента.


    1. PaulZi
      18.08.2016 18:07

      Добавил вариант с поворачивающими ручками:
      image


  1. punkkk
    18.08.2016 17:38

    Интересное решение, спасибо.)


  1. goodnickoff
    18.08.2016 23:20

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


    1. PaulZi
      18.08.2016 23:45

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


      1. PaulZi
        18.08.2016 23:49

        И как его стилизовать?


        1. goodnickoff
          19.08.2016 02:40

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


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


          1. PaulZi
            19.08.2016 13:45

            Парой строк js можно и свайп реализовать) Глянул я как это стилизуется — кошмар, сплошные вендорные псевдоэлементы. В общем, главное в моём посте — показать как можно застилить набор radio-инпутов, а применять или нет — это уже каждый сам решает.


  1. MetaDriver
    22.08.2016 11:04

    Прикольно, конечно, запилить всё это на css. Типа ради спорта. На ангуляре будет чуть меньше разметки, и на порядок меньше CSSa. Правда есть микродоза JS (таки пришлось объявить приложение и пустой контроллер)
    Пример тут (добавил снизу).
    // Встречный ангулар-прикол: классы для вращения не писал, а прямо в разметке забиндил формулы в атрибут style.


    1. PaulZi
      22.08.2016 11:08

      Ну я бы не сказал что у вас разметка сильно проще, и CSS много меньше. Сейчас в примере много CSS так как три варианта переключателей реализовано. А так на JS, конечно можно что-то «одной строчкой» сделать, только предварительно несколько десятков килобайт скриптов подключить)


  1. anttoshka
    22.08.2016 11:04

    Аналогично реализовывал переключение форм регистрации/входа. Честно говоря, думал, что эта практика известна =)


    1. PaulZi
      22.08.2016 11:08

      Идея не сложная, только почему то для такого элемента не распространена.


      1. anttoshka
        22.08.2016 11:56
        +1

        Довольно много кода css вместо нескольких строк на js думаю оказывают влияение на распространение.