Руководство по определению области видимости CSS-переменных в Angular
Самое замечательное в CSS-переменных – тот факт, что они могут применяться к псевдоклассам, таким как наведение и фокусировка. Еще одна особенность CSS-переменных, в которую мы пока не углублялись, заключается в том, что они могут применяться как на уровне документа – глобально, – так и локально, для отдельных элементах. В этом уроке мы рассмотрим некоторые плюсы и минусы глобального и локального определения области видимости переменных, а также узнаем, как применять стили к определенным элементам во время выполнения кода.
Особенности переменных уровня документа и элемента
Подобно скомпилированным расширениям CSS, таким как Sass и Less, CSS теперь использует свои собственные «чистые» переменные. Они определяются путем добавления двойного тире (–) перед именем переменной. После этого переменные CSS передаются в в функцию var() для получения доступа к ее значению. CSS-переменные объявляют в верхней части файла CSS, как показано в первом примере ниже, или на уровне правил, как это сделано во втором примере:
1) Объявление переменной на уровне документа
--main-bg-color: brown; .background { background-color: var(--main-bg-color); }
2) Объявление переменной на уровне правил
.wrapper:focus { background-color: --focus-color; }
При использовании метода setProperty() CSSStyleDeclaration на уровне документа в TypeScript или JavaScript переменная добавляется во встроенный атрибут стиля HTML тега:
document.documentElement.style.setProperty('--focus-color', this.textcolor);
Этот метод во многом похож на объявление пользовательского свойства в псевдоклассе :root в таблице стилей:
:root { --main-bg-color: brown; }
В этом методе нет ничего плохого, но вам следует помнить, что такой стиль затронет все элементы, которые ссылаются на переменную —main-bg-color. Хорошей практикой в веб-разработке считается объявление переменных в максимально ограниченной области, насколько это возможно. Этот совет подпадает под старое и общепринятое правило, которое гласит, что глобальные переменные – это плохой стиль программирования. Независимо от того, согласны ли вы с этим утверждением или нет, существует немало веских причин для ограничения области действия пользовательских свойств.
Предположим, нам нужно определить атрибут — focus-color для определенного div элемента в TypeScript коде:
@ViewChild("svgImage", { static: true }) private svgImageRef: ElementRef<HTMLDivElement>; //далее в коде svgImageRef.nativeElement.style.setProperty('--focus-color', 'gray');
Как можно увидеть на скриншоте, в результате этого действия CSS-переменная была добавлена в инлайновый стиль элемента:
Локальное объявление переменной
Возьмем, к примеру, фрагмент CSS, где объявляются две переменные цвета – одна меняет цвет фона при наведении курсора, вторая определяет оттенок рамки фокуса:
.news-image { $hoverColor: var(--hover-color); $focusColor: var(--focus-color); :hover { background-color: $hoverColor; } &:focus { outline: 2px solid $focusColor; } }
Поскольку переменные объявлены в правиле, их область действия ограничена элементами, соответствующими указанному селектору.
Напомним, что в примере привязки стилей CSS к событиям в приложениях Angular мы использовали глобальные CSS-переменные для создания трех цветных квадратов предварительного просмотра. Теперь давайте применим ту же технику к компоненту ленты новостей, но с использованием объявлений локальных переменных, как было показано выше.
Для создания ссылки на div, содержащий новость и изображение новостей в TypeScript коде, нам нужно добавить ссылку на шаблон #svgimage в тег div:
<div class="news-image" #svgImage tabindex="0" style="background-position: center; background-size: cover" [style.backgroundColor]="backgroundColor" > <svg> ... </svg> </div>
В верхней части класса FeedComponent вы увидите тот же способ объявления переменной @input, что и в предыдущем примере, а также ссылку на div-элемент с новостями и изображениями, любезно предоставленную декоратором ввода @ViewChild:
export class FeedComponent implements AfterContentInit, OnChanges { @Input('background-color') backgroundColor: string = 'blue'; @Input('hover-background-color') hoverBackgroundColor = 'cyan'; @Input('focus-border-color') focusBorderColor = '#CCCCCC'; @ViewChild('svgImage', { static: true }) private svgImageRef: ElementRef<HTMLDivElement> //... } export class FeedComponent implements AfterContentInit, OnChanges { @Input('background-color') backgroundColor: string = 'blue'; @Input('hover-background-color') hoverBackgroundColor = 'cyan'; @Input('focus-border-color') focusBorderColor = '#CCCCCC'; @ViewChild('svgImage', { static: true }) private svgImageRef: ElementRef<HTMLDivElement> //... }
Вероятно, вы заметили, что компонент ленты новостей теперь использует хуки жизненного цикла AfterContentInit и On Changes. Давайте рассмотрим их подробнее.
Инициализация переменной
Хотя вы можете получить доступ к переменной @Input в событии ngOnInit, вы не сможете ссылаться на элементы DOM до хука ngAfterContentInit. Объявление стиля CSSStyleDeclaration для svgImageRefсохраняется в частной переменной-члене класса для последующего использования. После этого для элемента svgImageRef устанавливаются цвета наведения и фокусировки:
private svgStyle: CSSStyleDeclaration; ngAfterContentInit(): void { this.svgStyle = this.svgImageRef.nativeElement.style; this.svgStyle.setProperty( '--hover-color', this.hoverBackgroundColor); this.svgStyle.setProperty( '--focus-color', this.focusBorderColor); }
Обновление значений CSS-переменных
Поскольку хук жизненного цикла ngAfterContentInit устанавливает CSS-переменные при первой загрузке приложения, нам необходимо использовать другой хук для обновления входных переменных, которое происходит каждый раз, когда пользователь вводит данные в поля ввода и нажимает кнопку «Применить». Здесь нам как раз пригодится ngOnChanges: он вызывается, когда изменяется любое свойство директивы, связанное с данными, – другими словами, всякий раз, когда изменяется значение @Input переменной компонента.
В жизненном цикле приложения ngOnChanges в первый раз срабатывает на самом раннем этапе, фактически до ngOnInit(). Поэтому нет смысла обновлять CSS-цвета при первом обходе, так как модель DOM в это время еще не будет готова. Мы можем проверить, является ли обход документа первым с помощью значения свойства firstChange. Это свойство равно true, когда ngOnChanges вызывается впервые. Помимо этого, необходимо проверить саму переменную, так как каждая связанная переменная возвращается вместе с соответствующим экземпляром SimpleChanges. Вот что мы получаем для каждой из наших переменных @Input при первом выполнении ngOnChanges:
Приведенный ниже код обновляет цвета наведения и фокусировки каждый раз, когда значение изменяется после первого запуска:
ngOnChanges(changes: SimpleChanges): void { if (changes.hoverBackgroundColor &&& !changes.hoverBackgroundColor.firstChange) { this.svgStyle.setProperty( '--hover-color', changes.hoverBackgroundColor.currentValue); } if (changes.focusBorderColor &&& !changes.focusBorderColor.firstChange) { this.svgStyle.setProperty( '--Focus-color', changes.focusBorderColor.currentValue); } }
Демонстрацию работы этого кода можно посмотреть на stackblitz.com:
Заключение
В этом уроке мы рассмотрели метод определения области видимости CSS-переменных, а также обсудили то, как применять пользовательские свойства к определенным элементам с помощью TypeScript и JavaScript. В конечном итоге, решение о том, объявлять ли ваши CSS-переменные глобально или внутри блоков правил, остается за вами. Я лично стараюсь определять все переменные настолько локально, насколько это возможно, потому что увеличить область определения всегда можно позже.
Источник: www.internet-technologies.ru