[CSS, HTML, Разработка веб-сайтов] Еще раз о визуализации input типа checkbox и radio. Для тех, кто забыл как

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
15-Июл-2020 16:32

Тема старая и уже, как выяснилось, подзабытая.
Недавно у меня была короткая работа по разработке ТЗ на модернизацию давно существующего проекта. И, в частности дело касалось стилизации пресловутых <input type="checkbox">. Выяснилось, что исполнитель, программист «на все руки» даже не понял, что я ему на словах объяснял как это сделать. Пришлось делать примеры и, как результат, появился этот текст.
Напомню, что сейчас checkbox и radiobox разные сайты изображают по-разному. Бывает, что не отмеченный input сразу и не разглядишь – такой он «дизайнерский красивый», а у последних версий Chrome выбранные checkbox стали гнусного цвета циан.
Итак, ситуация
Есть три компании, которые используют некий программный продукт, связанный с заказами, бухгалтерией, складом и пр. Работа с заказчиками, партнерами, исполнителями и пр.
Маркетологи и рекламщики тоже его используют. Что эта система делает – неважно, на чем написано – неважно.
А важно, что на сайте этого продукта есть много страниц с формами, на которых много input checkbox и radio.
Жалобы сотрудников
Директор: На большом экране плохо видно и незаметны «крыжики».
Главбух: На моем компе «крыжики» выглядят так, у сотрудниц иначе, дома тоже не так, а на планшете совсем иначе.
Маркетолог: А можно, так что бы некоторые не выбранные позиции были красными, а другие выбранные были зелеными?
И т.д., и т.п.
Итак, задача
  • Минимальными затратами и минимальными изменениями исправить внешний вид checkbox и radiobox.
  • Сделать стилизацию checkbox и radiobox для разных юзеров. Важно: это закрытый сайт, там «всё свои», «красоты» не нужны, а нужна эффективность восприятия.

Что нельзя
1. Серверную часть трогать нельзя.
2. Файлы javascript трогать нельзя, свой javascript вставлять нельзя.
3. Файлы css трогать нельзя.
А что можно
1. Править html шаблоны.
2. Создать файл стилей для всех юзеров.
4. Создать файл стилей для конкретного юзера или группы юзеров.
А что сделали можно сразу посмотреть на codepen.io, но лучше почитать дальше.
Предварительное изучение показало
1. Почти все <input type="checkbox"> имеют поле name, а которые не имеют, то у них есть id.
2. Все <input type="radio"> имеют поле name, некоторые имеют id.
3. Соответственно, в css к checkbox можно обращаться как по id, так и по name. К radio – или по id, или по номеру потомка у родителя.
Фрагменты исходного кода:
/* вариант 1 */
<tag><input type="checkbox"> Некий текст</tag>
/* вариант 2 */
<tag><input type="checkbox"> Некий текст<br>
<input type="checkbox"> Некий текст</tag>
/* вариант 3 */
...<label><input type="checkbox"> Некий текст</label>...
/* вариант 4 */
<td><input id="idxxx" type="checkbox"></td>
<td><label for="idxxx">Некий текст</label></td>

Так исправим код:
/* вариант 1 */
<tag><label class="new-input"><input type="checkbox"><s></s><span>Некий текст</span></label></tag>
/* вариант 2 */
<tag><label class="new-input"><input type="checkbox"><s></s><span>Некий текст</span></label><br>...</tag>
/* вариант 3 */
...<label class="new-input"><input type="checkbox"><s></s><span>Некий текст</span></label>...
/* вариант 4 */
<td><label class="new-input new-input-one"><input id="idxxx" type="checkbox"><s></s></label></td>
<td><label for="idxxx">Некий текст</label></td>

Всё тоже самое и для <input type="radio">, класс у LABEL тот же.
Что конкретно сделали?
  • Каждый input (корме варианта 3) обернули тэгом LABEL с нашим классом. Варианту 3 просто добавили класс.
  • Сразу после input вставили пустой тэг S. Так как сам input будет не видим, то это тэг будет визуализировать это input.
  • Сопроводительный текст обернули тэгом SPAN (кроме варианта 4). Этот тэг понадобиться, когда будем решать вопрос выравнивания визуального input относительно этого текста.
  • Варианту 4 добавили еще класс, что бы не осуществлять это выравнивание, раз сопроводительный текст стоит в другой ячейки таблицы. Строго говоря, надо было бы сделать на оборот – вариантам 1-3 добавить класс, отвечающий за выравнивание. Но, вариантов 1-3 гораздо больше, чем 4-го и что бы не раздувать html сделано так.

Риторические вопросы и риторические же ответы

SPL
1. Зачем тэг S? Ну, не нравится S – можно использовать любой другой строчный элемент. Просто он должен быть, его можно стилизовать в зависимости от состояния предшествующего input.
2. Почему тэги S и SPAN без классов? Ну, зачем раздувать html? Тем более, что не очевидно, что одна из конструкций ниже будет «работать» медленнее другой.
.new-input > S { }
  .new-input > .new-input-S {}

3. Как вы догадались, мне не нравятся идеи БЭМ, тем более идея «раздувать» html файл обилием упоминаний разных классов. В реальном проекте мы использовали только два класса – mni и mnio. :-))

Некоторые предварительные рассуждения и настройки css касательно box-sizing:border-box, нормализации LABEL, селекторов «A + B», «A ~ B» и «[attr]», псевдоклассов :checked, :disabled и ::before. Кто не уверен, что знает или хочет освежить знания смотрит под катом.

Предварительные рассуждения

SPL
1. Напомню, что в «старом» css (box-sizing:content-box) свойства width и height задают только ширину и высоту содержимого, а padding и border добавляются к этим значениям. box-sizing:border-box меняет схему так, что padding и border включаются в width и height.
2. Проверка показала, что в нашем случае используется старая модель, а менять «настройки» страниц запрещено. Не «наши» LABEL это простые строчные элементы, в них только текст. Поэтому стилизуем ВСЕ LABEL.
LABEL {
    box-sizing:border-box; cursor:pointer; user-select:none;
}
LABEL *,
LABEL *::before,
LABEL *::after {
    box-sizing:inherit;
}

Т.е., ставим box-sizing:border-box для тэга LABEL, всем его потомкам. Заодно ставим курсор и запрещаем выделение текст (что бы не мешало клику).
3. Комбинация селекторов «A + B» означает, что стили будут применяться только к селектору B, если он следует сразу ПОСЛЕ селектора A, т.е. только для первого B. С другой стороны, «A ~ B» означает, что ко всем селекторам B после селектора A, т.е. для первого и последующих.
Естественно, всё в пределах одного «родителя».
Как это будем использовать?
<label class="new-input"><input type="checkbox"><s></s><span>Некий текст</span></label>
<label class="new-input"><input type="radio"><s></s><span>Некий текст</span></label>

/* 1 */
.new-input > INPUT + S {}
.new-input > INPUT ~ SPAN {}
/* 2 */
.new-input > INPUT:not(:checked) + S {}
.new-input > INPUT:not(:checked) ~ SPAN {}
/* 3 */
.new-input > INPUT:checked + S {}
.new-input > INPUT:checked ~ SPAN {}
/* 4 */
.new-input > INPUT:disabled + S {}
.new-input > INPUT:disabled ~ SPAN {}
/* 5 */
.new-input > INPUT[type="radio"] + S {}

Первая группа – общие стили для тэгов S и SPAN.
Вторая группа – стили только когда INPUT НЕ выбран.
Третья – стили только когда INPUT выбран.
Четвертая – когда INPUT заблокирован.
И, наконец, пятая группа – общие стили для тэга S ТОЛЬКО, если он стоит после input radio.
Таким образом, можно изменять стили тэгов S и SPAN в зависимости от состояния input.
4. Поскольку у нас тэг S будет изображать из себя input, то самому input поставим display:none, его не будет видно, а тэг LABEL будет его переключать, а тэг S будет соответственно меняться. Почему не используем html свойство hidden у input? Потому, что на некоторых браузерах hidden у input «работает» не совсем верно, плюс не будем перегружать html файл.

Итак, начинаем визуализацию input
Пример N 1. Самый простой – используем алфавитные символы
html код тот же, а css будет такой:
/* s1 */
.new-input > INPUT + S::before {
  content: "c";
}
/* s2 */
.new-input > INPUT:checked + S::before {
  content: "V";
}
/* s3 */
.new-input > INPUT[type="radio"] + S::before {
  content: "r";
}
/* s4 */
.new-input > INPUT[type="radio"]:checked + S::before {
  content: "X";
}
/* s5 */
.new-input > INPUT:disabled + S::before {
  opacity: 0.5;
}
/* s6 */
.new-input > S {
  text-decoration: none;
  margin-left: 3px;
  margin-right: 6px;
}
/* s7 */
.new-input > S::before {
  display: inline-block;
  width: 1.25em;
  text-align: center;
  color: #fafafa;
  background-color: #37474f;
}
/* s8 */
.new-input > INPUT[type="radio"] + S::before {
  border-radius: 50%;
}

Тэг S буде визуализировать input. Но мы «разделим» его по функционалу: сам тэг S будет отвечать за размещение в LABEL и выравнивание относительно следующего SPAN.
А псевдоэлемент S::before разместится внутри тэга S и будет изображать из себя input.
Строка s1 определяет, какой символ будет помещен в S::before когда input не выбран. В принципе надо было бы написать «.new-input > INPUT:not(:checked) + S::before», но некоторые браузеры (например, IE), подобную конструкцию могут и не исполнить.
Строка s2 определяет символ, когда input выбран.
Строки s3 и s4 делают то же для input radio.
Строка s5 описывает, что будет если input заблокирован – в данном случае тэг S будет наполовину прозрачным.
Строка s6 определяет выравнивание, в данном случае дает отбивку слева и справа (только в этом примере). Плюс, убирает штатное перечеркивание.
Строка s7 делает квадратик, s8 превращает его в кружок для input radio.
Пример N 1 можно посмотреть на codepen.io. Там представлены нативные input и новые. Первые можно убрать.

Чуток подробнее про display&#58; inline-block, font-size, line-height

SPL
Конченая высота строки текста определяется на основе заданных font-size, line-height. При единичном line-height – высота будет по font-size, при числовом line-height – высота будет по их произведению или, при указании единиц измерения для line-height – высоту определит максимальное значение. В примере указан line-height:1.25, поэтому и у S::before указано width:1.25em.
Для S::before указано display: inline-block – в этом случае S::before «внутри» себя будет блоком (можно указать ширину, высоту, рамки и пр.), а «снаружи» он останется строчным элементом. В дальнейшем об этом будет подробнее.

Вопрос:
Может можно использовать специальные символы? Типа вот этих:
□ ■ ▢ ▣ ○ ● ◎◉
Задать им нужный размер и всё. Нет?
Ответ:
Можно. Но не нужно. Ибо будет большой геморрой и танцы с бубнами по заданию нужного размера, выравнивания по вертикали, обрезке по высоте и прочее. Плюс, разные браузеры с этими символами работают по-разному.
Мы пошли другим путем. Хотя в финальном примере есть реализация этой идеи.
Пример N 2. «Рисуем» элементы input средствами css
html код тот же, а css будет такой:
/* s1 */
.new-input > S::before {
  content: "";
  display: inline-block;
  width: 0.75em;
  height: 0.75em;
  border: 1px solid currentColor;
  padding: 2px;
  background-clip: content-box;
  border-radius: 20%;
}
/* s2 */
.new-input > INPUT[type="radio"] + S::before {
  border-radius: 50%;
}
/* s3 */
.new-input > INPUT:checked + S::before {
  background-color: currentColor;
}
/* s4 */
.new-input > INPUT:disabled + S::before {
  opacity: 0.5;
}
/* s5 */
.new-input > S {
  text-decoration: none;
  margin-left: 3px;
  margin-right: 6px;
}

Строка s1 определяет S::before для визуализации input. Это будет inline-block, ширина и высота которого установлена в 0.75em, что примерно равно высоте прописной буквы и зависит от font-size родителя. Задана тонкая рамка текущим цветом, внутренняя отбивка, небольшое скругление углов. И – самое важное! – установлено свойство background-clip:content-box. Это очень интересное свойство – если будет установлен background-color, то он закрасит только контентную часть и не затронет отбивку (padding). Что нам и надо.
Строка s2 для input типа radio делает S::before круглым.
Строка s3 для отмеченного input устанавливает для S::before background-color текущим цветом. Т.е., «рисует» внутри квадратик или кружок.
Строка s4 отрабатывает блокировку input, строка s5 дает отбивки слева и справа.
Преимущества этого метода
  • Всё очень просто. Работает на всех браузерах. Даже у IE10 (в эмуляции у 11-го).
  • Можно раскрашивать по своему усмотрению.
  • Раз S::before это inline-block, то он сидит на попе базовой линии ровно и никуда с нее не слезает. Если он по высоте будет больше текста, то просто увеличит высоту строки и останется на базовой линии.
  • Раз визуализация input находится внутри тэга S, то его можно легко позиционировать и выравнивать.
  • Размеры S::before в em дают возможность задавать его размер относительно размера текста подписи. Можно, к примеру, поставить предельные значения высоты и ширины.

Недостатки этого метода
В основном в использовании размеров в em. Дело в том, что может возникнуть ситуация когда ширина и высота при расчете (из em в px) будет иметь дробное значение. На обычных компьютерах с обычным экраном округление может произойти не корректно. Например, размеры 12.8px на 12.8px у той же Мозилы могут стать как 13px на 12px. Тогда надо ставить фиксированные размеры. Хотя на современных мониторах и видеокартах, ноутбуках, на планшетах и смартфонах этого не происходит из-за того, что точка (пиксель) браузера состоит из нескольких пикселей экрана.
Пример N 2 можно посмотреть на codepen.io. Там представлены нативные input и новые. Первые можно убрать.
Итак, первую задачу – визуализацию input – выполнили. Переходим к избранной «раскраске».
Раскрашиваем input
html для примера:
<label class="new-input"><input name="chb1" type="checkbox" ...><s></s><span>Некий текст</span></label>
<label class="new-input"><input id="rb1" type="radio" ...><s></s><span>Некий текст</span></label>

К input типа checkbox будем обращаться по name, к radio по id.
Всё красим в синий
/* только input */
.new-input > INPUT[name="chb1"] + S,
.new-input > INPUT#rb1 + S {
  color: #0091ea;
}
/* только text */
.new-input > INPUT[name="chb1"] ~ SPAN,
.new-input > INPUT#rb1 ~ SPAN {
  color: #0091ea;
}
/* или всё */
.new-input > INPUT[name="chb1"] ~ *,
.new-input > INPUT#rb1 ~ * {
  color: #0091ea;
}

Помним о специфичности в css, эти стили будут более специфичны, чем базовые и сработают обязательно. Чем они отличаются от описанных выше? Тем, что применяются только к избранным input – к тем, что имеет указанное значение name и id.
Тут всё хорошо кроме того, что не выбранные input будут не очень хорошо глядеться – тонкая синяя рамка мало заметна.
Красим в зеленый, когда input выбран
/* только input */
.new-input > INPUT[name="chb1"]:checked + S,
.new-input > INPUT#rb1:checked + S {
  color: #00c853;
}
/* только text */
.new-input > INPUT[name="chb1"]:checked ~ SPAN,
.new-input > INPUT#rb1:checked ~ SPAN {
  color: #00c853;
}
/* или всё */
.new-input > INPUT[name="chb1"]:checked ~ *,
.new-input > INPUT#rb1:checked ~ * {
  color: #00c853;
}

Первый вариант, на мой взгляд, не очень хорош – зеленым будут и рамка, и внутренний квадратик/кружок. Можно раскрасить только его.
/* только input и только внутри */
.new-input > INPUT[name="chb1"]:checked + S::before,
.new-input > INPUT#rb1:checked + S::before {
  background-color: #00c853;
}

Красим в красный, когда input НЕ выбран
/* только input */
.new-input > INPUT[name="chb1"]:not(:checked) + S,
.new-input > INPUT#rb1:not(:checked) + S {
  color: #d50000;
}
/* только text */
.new-input > INPUT[name="chb1"]:not(:checked) ~ SPAN,
.new-input > INPUT#rb1:not(:checked) ~ SPAN {
  color: #d50000;
}
/* или всё */
.new-input > INPUT[name="chb1"]:not(:checked) ~ *,
.new-input > INPUT#rb1:not(:checked) ~ * {
  color: #d50000;
}

Логика понятна? Можно и дальше делать более сложные конструкции.
Например, при не выбранном input текст должен быть красным и жирным, а при выбранном внутренний элемент input и текст должен быть зеленым. Элементарно!
/* текст, когда нет выбора */
.new-input > INPUT[name="chb1"]:not(:checked) ~ SPAN,
.new-input > INPUT#rb1:not(:checked) ~ SPAN {
  color: #d50000;
  font-weight: bold;
}
/* внутренний элемент input, когда выбран */
.new-input > INPUT[name="chb1"]:checked + S::before,
.new-input > INPUT#rb1:checked + S::before {
  background-color: #00c853;
}
/* текст, когда выбран */
.new-input > INPUT[name="chb1"]:checked ~ SPAN,
.new-input > INPUT#rb1:checked ~ SPAN {
  color: #00c853;
}

А, к примеру, надо обработать целую группу input (10-15 штук). Что бы не писать кучу строк можно найти их общего родителя (.parent_element) и сократить условие.
.parent_element > .new-input > INPUT:not(:checked) ~ SPAN {
  color: #d50000;
  font-weight: bold;
}
.parent_element > .new-input > INPUT:checked + S::before {
  background-color: #00c853;
}
.parent_element > .new-input > INPUT:checked ~ SPAN {
  color: #00c853;
}

Всё можно посмотреть в финальном примере на codepen.io
Вот, вроде как, и всё. Осталось только «почесать родимые пятна» перфекциониста – проблемы выравнивания.
Выравнивание визуального input и сопроводительного текста
Для начала напомню общеизвестные вещи на тему размещения текста, форматирования и прочего. Всё под катом.

Общеизвестные вещи

SPL
Буду стараться не применят специальные термины, ибо в дизайне, верстке и css они иногда отличаются. Всё простыми словами.
1. Свойство font-size не определяет размер букв, а только размер знакоместа. Есть базовая линия (baseline), по которой расположены «нормальные» буквы. У «ненормальных» – g ц – нижние элементы «свисают» ниже её. Есть линия капители (cap height) – это верхняя граница «нормальной» прописной (заглавной) буквы. У «ненормальных» – Ё Й – верхние элементы «вылезают» выше её. Иными словами, размер прописной буквы это расстояние от базовой линии до капители, а знакоместо это чуть больше сверху и снизу. Обычно в «нормальных» шрифтах высота капители это 75% от высоты знакоместо. К примеру, font-size:16px, а размер буквы Н у шрифта Arial будет 12px. Но, бывают «специалисты» у шрифтов которых всё не так.
2. Свойство line-height определяет высоту строки. Если его вычисленное значение больше, чем указано в font-size, то браузер разместит текст так, что бы нормальная прописная буква была по середине высоты строки. Есть нюансы, но тут они не важны.
3. Соответственно, в нашем случае тэги S и SPAN должны иметь одинаковые значения font-size и line-height желательно заданные где-то выше у родителей. В нашем случае в примерах font-size:16px и line-height:1.25. Поэтому в примере N1 у S::before ширина указана 1.25em, а высота у него определяется автоматически. А в примере N2 (и финальный пример) – у S::before ширина и высота 0.75em, что бы был по высоте с прописную букву. Задав другое значение font-size ничего менять не надо. Естественно, эту величину надо подогнать под конкретный шрифт.
4. Если перед текстом стоит какая-то квадратная или круглая «штучка», то любой дизайнер скажет, что она должна быть по высоте с прописную букву. А отбивка между ними должна быть в определенных процентах от размера шрифта. Если высота меньше высоты буквы, то она должна быть визуально значительно меньше, но не меньше 50%. Если больше, то тоже визуально значительно больше, но не больше 150%. А вот чуть-чуть, на пару пикселей больше/меньше – это ужас-ужас! Ну, и расположена эта штучка должна быть на базовой линии или по середине без всяких там чуть-чуть.
Зачем я это упомянул? А затем, что перфекционисту глаза режет, когда input криво стоит рядом с текстом — или прилипает, или далеко, или чуть меньше, или чуть больше. Мы так делать не должны!

Что будет, если сопроводительный текст в SPAN будет выведен в две или три строки? Очевидно, что он «залезет» под input. Это не красиво, надо исправить.
Один древний метод такой: тэгу S делаем float:left, а тэгу SPAN display:block и overflow:hidden.
Получится колонка текста. Подразумевается, что у кого-то из них будет соответствующий margin, что даст отбивку между ними. Ну, ещё добавляется геморрой с прекращением float после SPAN. Мы пойдем современным путем – применим flexbox. Он тут совершенно к месту.
.new-input {
  display: flex;
  flex-direction: row;
  align-items: start;
}
.new-input > S {
  margin-right: 4px;
  flex: 0 0 auto;
}
.new-input > SPAN {
  flex: 0 1 auto;
}

В этом случае тэг LABEL (который .new-input) будет flex, S и SPAN будут блоками, разместятся вверху LABEL. Текст в SPAN в случае необходимости будет в несколько строк. Вот из-за этого визуальный input описали в S::before. Независимо от высоты SPAN S::before будет расположен на одной базовой линии с первой строкой SPAN. Как вариант можно было указать align-items:center – тогда при однострочном SPAN визуальный input был бы вверху, а при двух строках – посередине, а при трех – у второй строки. В финальном примере можно переключать расположение input.
Вот и всё
Надеюсь, было интересно и кому-нибудь полезно. Прошу, не сильно меня ругать – это мой первый опыт на Хабр.
Пример N 1 – просто демонстрация взаимодействия изменения input и соседнего элемента.
Пример N 2 – визуализация input средствами css, как основа решения.
Финальный пример – всё описанное вместе.
Про конкретную реализацию
Там были обширные формы, где блоки полей возможные для правки конкретным пользователям выделялись слабым фоном, а остальным input имели свойство disabled и служили только для информации. Поэтому стиль «.new-input > INPUT:disabled + S::before» не применяли.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_css, #_html, #_razrabotka_vebsajtov (Разработка веб-сайтов), #_input, #_checkbox, #_radio, #_flex, #_flexbox, #_css, #_html, #_razrabotka_vebsajtov (
Разработка веб-сайтов
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 23-Ноя 02:18
Часовой пояс: UTC + 5