Русский
Русский
English
Статистика
Реклама

Input

Из песочницы Еще раз о визуализации input типа checkbox и radio. Для тех, кто забыл как

15.07.2020 14:22:53 | Автор: admin
Тема старая и уже, как выяснилось, подзабытая.

Недавно у меня была короткая работа по разработке ТЗ на модернизацию давно существующего проекта. И, в частности дело касалось стилизации пресловутых <input type="checkbox">. Выяснилось, что исполнитель, программист на все руки даже не понял, что я ему на словах объяснял как это сделать. Пришлось делать примеры и, как результат, появился этот текст.

Напомню, что сейчас checkbox и radiobox разные сайты изображают по-разному. Бывает, что не отмеченный input сразу и не разглядишь такой он дизайнерский красивый, а у последних версий Chrome выбранные checkbox стали гнусного цвета циан.

Итак, ситуация


Есть три компании, которые используют некий программный продукт, связанный с заказами, бухгалтерией, складом и пр. Работа с заказчиками, партнерами, исполнителями и пр.

Маркетологи и рекламщики тоже его используют. Что эта система делает неважно, на чем написано неважно.

А важно, что на сайте этого продукта есть много страниц с формами, на которых много input checkbox и radio.

Жалобы сотрудников


Директор: На большом экране плохо видно и незаметны крыжики.
Главбух: На моем компе крыжики выглядят так, у сотрудниц иначе, дома тоже не так, а на планшете совсем иначе.
Маркетолог: А можно, так что бы некоторые не выбранные позиции были красными, а другие выбранные были зелеными?
И т.д., и т.п.

Итак, задача


  1. Минимальными затратами и минимальными изменениями исправить внешний вид checkbox и radiobox.
  2. Сделать стилизацию 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 тот же.

Что конкретно сделали?


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

Риторические вопросы и риторические же ответы
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. Кто не уверен, что знает или хочет освежить знания смотрит под катом.

Предварительные рассуждения
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: inline-block, font-size, line-height
Конченая высота строки текста определяется на основе заданных 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 дает отбивки слева и справа.

Преимущества этого метода

  1. Всё очень просто. Работает на всех браузерах. Даже у IE10 (в эмуляции у 11-го).
  2. Можно раскрашивать по своему усмотрению.
  3. Раз S::before это inline-block, то он сидит на попе базовой линии ровно и никуда с нее не слезает. Если он по высоте будет больше текста, то просто увеличит высоту строки и останется на базовой линии.
  4. Раз визуализация input находится внутри тэга S, то его можно легко позиционировать и выравнивать.
  5. Размеры 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 и сопроводительного текста


Для начала напомню общеизвестные вещи на тему размещения текста, форматирования и прочего. Всё под катом.

Общеизвестные вещи
Буду стараться не применят специальные термины, ибо в дизайне, верстке и 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 не применяли.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru