Перед тим як перейти до того, як JavaScript взаємодіє зі стилями та класами, є одне важливе правило. Сподіваємося, воно достатньо очевидне, проте ми все одно повинні це згадати.
Загалом існує два способи стилізації елемента:
Створити клас в CSS і додати його до елемента: <div class="...">
2. Записати правила безпосередньо в атрибут style
: <div style="...">
.
JavaScript дозволяє змінювати як класи, так і style
властивості.
Зазвичай ми маємо віддавати перевагу CSS-класам над style
. Пряме задання стилів слід використовувати лише в тих випадках, якщо класи “не можуть з цим впоратися”.
Для прикладу, допустимо використовувати style
, якщо ми динамічно обчислюємо координати елемента та хочемо встановлювати їх через JavaScript, як от:
let top = /* складні обрахунки */;
let left = /* складні обрахунки */;
elem.style.left = left; // наприклад, '123px', розраховується під час виконання
elem.style.top = top; // наприклад '456px'
В інших випадках, як-от зробити текст червоним або додати іконку на фон – опишіть це в CSS, а потім додайте клас (JavaScript легко з цим впорається). Такий підхід більш гнучкий, і простіший у підтримці.
className та classList
Зміна класу є однією з найчастіше використовуваних дій у скриптах.
В давні часи JavaScript мав певні обмеження: зарезервоване слово, таке як "class"
, не могло бути властивістю об’єкта. Цього обмеження наразі більше немає, проте на той час було неможливо мати властивість "class"
, як от elem.class
.
Тому для класів було додано схожу на вигляд властивість "className"
: elem.className
відповідає атрибуту "class"
.
Наприклад:
<body class="main page">
<script>
alert(document.body.className); // main page
</script>
</body>
Коли ми присвоюємо щось властивості elem.className
, це значення замінює весь рядок класів. Інколи це саме те, що нам потрібно, проте значно частіше ми хочемо просто додати чи видалити один клас.
Для цього існує інша властивість, а саме elem.classList
.
Властивість elem.classList
– це спеціальний об’єкт, який містить методи для додавання, видалення або перемикання окремого класу.
Наприклад:
<body class="main page">
<script>
// додати клас
document.body.classList.add('article');
alert(document.body.className); // main page article
</script>
</body>
Таким чином можна оперувати як цілим рядком класів за допомогою властивості className
, так і окремими класами через classList
. Що саме обрати – залежить від потреб конкретної ситуації.
Методи classList
:
elem.classList.add/remove("class")
– додати/видалити клас.elem.classList.toggle("class")
– додає клас, якщо він не існує, інакше видаляє його.elem.classList.contains("class")
– перевіряє, чи існує переданий клас, відповідно повертаєtrue/false
.
Крім того, classList
– це ітерований об’єкт, тому можна легко вивести перелік всіх класів циклом for..of
, як показано далі:
<body class="main page">
<script>
for (let name of document.body.classList) {
alert(name); // main, а за ним page
}
</script>
</body>
Стилі елемента
Властивість elem.style
– це об’єкт, вміст якого відповідає тому, що записано в атрибуті "style"
. Встановлення elem.style.width="100px"
працює точнісінько так само, як рядок width:100px
записаний в атрибут style
.
Для властивостей, які називаються кількома словами, використовується верблюдячий регістр (camelCase):
background-color => elem.style.backgroundColor
z-index => elem.style.zIndex
border-left-width => elem.style.borderLeftWidth
Наприклад:
document.body.style.backgroundColor = prompt('background color?', 'green');
Властивості з браузерними префіксами – наприклад, -moz-border-radius
, -webkit-border-radius
також підпорядковуються цьому правилу: дефіс означає верхній регістр.
Наприклад:
button.style.MozBorderRadius = '5px';
button.style.WebkitBorderRadius = '5px';
Скидання властивості в elem.style
Інколи ми хочемо призначити певну стильову властивість, а пізніше видалити її.
Наприклад, щоб приховати елемент, ми можемо встановити властивість elem.style.display = "none"
.
Потім може виникнути потреба видалити style.display
так, як наче вона не була встановлена. Замість delete elem.style.display
ми повинні присвоїти їй порожній рядок: elem.style.display = ""
.
// якщо ми запустимо цей код, елемент <body> "моргне"
document.body.style.display = "none"; // сховати
setTimeout(() => document.body.style.display = "", 1000); // назад до нормального стану
Якщо ми встановлюємо для style.display
порожній рядок, то браузер застосовує CSS-класи та свої вбудовані стилі нормально, ніби такої властивості як style.display
взагалі не було.
Також для цього існує спеціальний метод elem.style.removeProperty('style property')
. Отже, ми можемо видалити таку властивість наступним чином:
document.body.style.background = 'red'; // ставить колір red до background
setTimeout(() => document.body.style.removeProperty('background'), 1000); // видаляє background через 1 секунду
style.cssText
Зазвичай, style.*
використовується для встановлення окремих властивостей стилю. Немає можливості задати весь стиль, як от div.style="color: red; width: 100px"
, оскільки div.style
– це об’єкт, і він доступний лише для читання.
Існує спеціальна властивість style.cssText
, яка дає змогу встановлювати повний стиль елемента як рядок:
<div id="div">Кнопка</div>
<script>
// тут можна встановити такі спеціальні прапорці стилю, як "important"
div.style.cssText=`color: red !important;
background-color: yellow;
width: 100px;
text-align: center;
`;
alert(div.style.cssText);
</script>
Ця властивість рідко використовується, оскільки присвоєння стилів у такий спосіб видаляє всі наявні стилі: тобто воно не додає, а замінює їх. Може випадково видалити щось потрібне. Однак можна спокійно використовувати її для нових елементів, коли наперед відомо, що там немає ніяких наявних стилів, які можна було б випадково видалити.
Того самого можна досягнути шляхом встановлення значення атрибута: div.setAttribute('style', 'color: red...')
.
Пам’ятайте про одиниці вимірювання
Не забувайте додавати одиниці вимірювання CSS до значень.
Наприклад, ми повинні встановлювати elem.style.top
не на 10
, а на 10px
. Інакше воно не працюватиме:
<body>
<script>
// не працює!
document.body.style.margin = 20;
alert(document.body.style.margin); // '' (порожній рядок, присвоєння проігноровано)
// тепер додамо одиниці виміру CSS (px) - і це працює
document.body.style.margin = '20px';
alert(document.body.style.margin); // 20px
alert(document.body.style.marginTop); // 20px
alert(document.body.style.marginLeft); // 20px
</script>
</body>
Зауважте, будь ласка: браузер в останніх рядках “розпаковує” властивість style.margin
і від неї обчислює значення властивостей style.marginLeft
та style.marginTop
.
Обчислені стилі: getComputedStyle
Отже, змінити стиль легко. Проте як його прочитати?
Уявімо собі, що ми хочемо дізнатися розміри, зовнішні відступи, та колір елементу. Як це зробити?
Властивість style
працює лише зі значенням атрибута "style"
, без врахування CSS-каскаду.
Тобто за допомогою elem.style
неможливо прочитати щось, що прийшло з CSS-класів.
Наприклад, ось тут властивість style
не бачить зовнішніх відступів:
<head>
<style> body { color: red; margin: 5px } </style>
</head>
<body>
Червоний текст
<script>
alert(document.body.style.color); // порожньо
alert(document.body.style.marginTop); // порожньо
</script>
</body>
…Але що робити, якщо ми хочемо, скажімо, збільшити розмір зовнішнього відступу на 20px
? Для цього нам потрібно знати актуальне значення.
Для цього існує інший метод: getComputedStyle
.
Синтаксис виглядає так:
getComputedStyle(element, [pseudo])
- element
- Елемент, значення властивості якого потрібно прочитати.
- pseudo
- Вказується, якщо потрібен стиль псевдоелемента, наприклад:
::before
. Порожній рядок, або опущений аргумент означатимуть, що буде опрацьовано сам елемент.
В результаті виклику методу буде повернено об’єкт зі стилями, подібно до elem.style
, але зі врахуванням всіх класів CSS.
Наприклад:
<head>
<style> body { color: red; margin: 5px } </style>
</head>
<body>
<script>
let computedStyle = getComputedStyle(document.body);
// тепер звідти можна прочитати значення зовнішнього відступу та кольору
alert( computedStyle.marginTop ); // 5px
alert( computedStyle.color ); // rgb(255, 0, 0)
</script>
</body>
CSS має дві концепції:
- Обчислене (computed) значення стилю – це значення після застосування всіх CSS-правил і наслідування, результат CSS-каскаду. Воно може виглядати як
height:1em
чиfont-size:125%
. - Кінцеве (resolved) значення стилю – це значення, яке безпосередньо застосовується до елементу. Такі значення, як
1em
чи125%
– відносні. Браузер бере обчислене значення, і перераховує все у фіксованих і абсолютних одиницях, наприклад:height:20px
чиfont-size:16px
. Для геометричних властивостей кінцеві значення можуть бути числами з рухомою комою, як отwidth:50.5px
.
Колись давно метод getComputedStyle
був створений саме для отримання обчислених значень, проте кінцеві значення виявились значно зручнішими, тому стандарт було змінено.
Тому станом на сьогодні метод getComputedStyle
насправді повертає кінцеве значення властивості, для геометрії зазвичай виражене в px
.
getComputedStyle
вимагає повної назви властивостіСлід завжди запитувати точну назву властивості, значення якої потрібно отримати, як paddingLeft
, marginTop
чи borderTopWidth
. Інакше коректний результат не гарантовано.
Наприклад, якщо на елементі задано властивості paddingLeft/paddingTop
, що ми отримаємо, запитавши значення getComputedStyle(elem).padding
? Нічого, чи може якесь “згенероване” значення наявних полів? Стандарту для цього не існує.
:visited
(відвідані) посилання – приховуються!Відвідані посилання можна пофарбувати, використовуючи CSS-псевдоклас :visited
.
Проте метод getComputedStyle
не дає доступу до цього кольору, бо інакше будь-яка вебсторінка могла б визначити, чи користувач відвідував якесь посилання, просто створивши це посилання на сторінці та перевіривши його стилі.
JavaScript не бачить стилі, застосовані через селектор :visited
. На додачу, також є певні обмеження в CSS, які забороняють застосовувати на :visited
будь-які стилі, що змінюють геометрію елемента. Це все робиться з метою гарантувати, що для шкідливої сторінки не існує ніякого побічного способу перевірити, чи посилання відвідувалось, і таким чином поламати приватність користувача.
Підсумки
Існує дві властивості DOM для керування класами:
className
– рядкове значення, гарно підходить для керування всім набором класів елемента в цілому.classList
– об’єкт з методамиadd/remove/toggle/contains
, підходить для роботи з окремими класами.
Для зміни стилів:
-
Властивість
style
– об’єкт зі стилями, записаними верблюдячим регістром (camelCase). Читання і запис у неї має такий самий зміст, як і модифікація окремих властивостей в атрибуті"style"
. Щоб побачити, як застосовуєтьсяimportant
та інші рідкісні речі – на MDN описано перелік методів. -
Властивість
style.cssText
показує атрибут"style"
в цілому, весь рядок стилів.
Для зчитування кінцевих стилів (з врахуванням всіх класів, після застосування всього CSS і обчислення остаточних значень):
- Метод
getComputedStyle(elem, [pseudo])
повертає об’єкт, подібний до властивостіstyle
, з кінцевими значеннями. Виключно для читання.
Коментарі
<code>
, для кількох рядків – обгорніть їх тегом<pre>
, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)