Пользовательские CSS-атрибуты как механизм передачи данных из JavaScript в CSS
Перевод статьи Using CSS Custom attributes generated by JavaScript as a handover mechanism с сайта medium.com для css-live.ru, автор — Кристиан Хайльман
Обновление: изначально я слишком упростил, что пользовательские атрибуты не поддерживают конкатенацию, но благодаря Шиме Видасу, Брайану Карделу и Грегу Уитфорту ситуация прояснилась.
Нововведения в CSS стали понемногу размывать границы между ним и JavaScript. CSS был статичным языком, отвечающим за определение цветов и внешний вид, а не за интерактивность. Размеры в процентах помогали в какой-то мере подстраиваться под окружение, но реагировать на изменения было прерогативой JavaScript.
В былые времена HTML отвечал за структуру, CSS — за внешний вид, а JavaScript — за интерактивность. Или, как я сказал в своей книге в 2006 году, если бы сайт был фильмом, то HTML был бы сценарием, CSS — операторской и режиссерской работой, а JavaScript — спецэффектами.
Сегодня CSS способен на гораздо большее. У нас есть анимации, переходы, calc()
и гораздо более гибкие единицы измерения вроде em
, rem
, vw
, vh
и прочих. А также интерактивность с псевдоселекторами вроде hover
, focus
и состояний интерактивных элементов типа кнопок. Можно даже применить хак с чекбоксами (прим. перев.: а можно и без чекбоксов) и написать полноценную игру на чистом CSS.
Это здорово! Энтузиастам CSS теперь наверняка хватит терпения и знаний, чтобы анимация или отклик на действие пользователя выглядели и вели себя именно так, как надо. CSS-движки отвечают за хорошую производительность и не убивают интерактивность или заряд батареи устройства пользователя. Производители браузеров могут сосредоточиться на оптимизации движка, а не переваливать всю ответственность за плавную работу на разработчика.
Но всё же возможности CSS не безграничны, и в некоторых случаях без JavaScript не обойтись. Частый пример – когда надо отразить текущее состояние какого-то действия в браузерном окне, или реагировать на что-то такое, чего спецификации CSS не предусмотрели.
Полностью переключаться на JavaScript в таком случае кажется мне каким-то механическим рефлексом, и для меня логичнее найти способ взаимодействия JavaScript и CSS. Прочитать значение на JavaScript и как-нибудь добиться, чтобы его увидел CSS.
В прошлом единственным способом сделать это было хранение классов у родительских элементов и удаление классов при срабатывании определённых условий. Но с пользовательскими свойствами («CSS-переменными») взаимодействие между JavaScript и CSS становится гораздо легче.
Пользовательские свойства позволяют устанавливать «переменные» в CSS и использовать их позже. К примеру:
::root { --company-blue: #369; } h1 { color: var( --company-blue); }
Пользовательские свойства ведут себя иначе, чем CSS-переменные в препроцессорах. Они тоже поддерживают конкатенацию, но есть ограничения.
Спасибо Шиме Видасу, который показал рабочее демо в Твиттере, и Брайану Карделлу, указавшему на обсуждение в рабочей группе по CSS.
Как поясняет мой коллега Грег Уитворт:
Насчет пользовательских свойств это неверно. Проблема, о которой, видимо, ты говоришь – это ограничения CSS вообще. Хотя Шиме уже показал, что конкатенация возможна, но, пожалуй, не во всех сценариях, где действительно надо просто соединить строки (напр. из “foo” calc(5+8) получится \”foo\” calc(58), так как это невалидный calc, эти части тоже преобразуются в строки, но с экранированными кавычками). Всё в переменных разбивается на токены, так что строка это или нет – всё зависит от того, как это значение распознает токенизатор. Если в идентификаторе нет синтаксических ошибок, но при этом он не совпадает ни с чем известным, то для CSS он преобразуется в строку. Всё, что передается в JS, преобразуется в строку. Вот JSbin-пример, где это хорошо видно.
Простейший способ изменить пользовательские CSS-свойства — умножить их на какое-то значение с помощью calc().
::root { --startwidth: 200; } h1 { width: calc(var( --startwidth) * 1px); } h2 { width: calc(var( --startwidth) * 0.5px); }
Теперь, раз пользовательские свойства можно определять в JavaScript и добавлять их любому элементу, это отличный способ сделать так, чтобы JavaScript только читал значение, а остальное взял на себя CSS. Например, если нужно узнать положение скролла на странице, можно прочитать его обработчиком события в JavaScript и обновить пользовательский CSS-атрибут:
window.addEventListener('scroll', (e) => { document.body.style.setProperty('--scrolly', window.scrollY); });
CSS:
h1 { position: fixed; width: calc(var( --scrolly) * 1px); background: #339; }
Можете опробовать это в данном JSBin.
Это далеко не готовый пример, но меня радует уже то, что с помощью JavaScript можно выходить за рамки доступного для CSS, и при этом CSS может по-прежнему заведовать и управлять интерактивностью.
P.S. Это тоже может быть интересно:
Спасибо!
отлично. спасибо
Спасибо, сохранил
Благодарю, то, что нужно!