Перед тим як перейти до того, як JavaScript взаємодіє зі стилями й класами, є одне важливе правило. Сподіваємося, воно достатньо очевидне, проте його все одно необхідно згадати.
Загалом існує два способи стилізувати елемент:
- Створити клас в CSS і додати його до елемента:
<div class="...">
- Записати правила безпосередньо в атрибут
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
.
Для властивостей, які називаються кількома словами, використовується верблюдячийРегістр:
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.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
.
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 до значень.
Наприклад, не слід встановлювати значення 10
властивості elem.style.top
, проте радше 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
? Нічого, чи може якесь “згенероване” значення наявних полів? Тут немає жодного стандартного правила.
Також існують інші непослідовності. Для прикладу, деякі браузери (Chrome) покажуть 10px
в документі, наведеному нижче, а інші (Firefox) – цього не зроблять:
<style>
body {
margin: 10px;
}
</style>
<script>
let style = getComputedStyle(document.body);
alert(style.margin); // порожній рядок у Firefox
</script>
:visited
(відвідані) посилання – приховуються!Відвідані посилання можна забарвлювати, використовуючи CSS-псевдоклас :visited
.
Проте метод getComputedStyle
не дає доступу до цього кольору, бо інакше будь-яка вебсторінка могла б визначити, чи користувач відвідував якесь посилання, просто створивши це посилання на сторінці та перевіривши його стилі.
JavaScript не бачить стилі, застосовані через селектор :visited
. На додачу, також є певні обмеження в CSS, які забороняють застосовувати на :visited
будь-які стилі, що змінюють геометрію елемента. Це все робиться з метою гарантувати, що для шкідливої сторінки не існує ніякого побічного способу перевірити, чи посилання відвідувалось, і таким чином поламати приватність користувача.
Підсумки
Існує дві властивості DOM для керування класами:
className
– рядкове значення, гарно підходить для керування всім набором класів елемента в цілому.classList
– об’єкт з методамиadd/remove/toggle/contains
, підходить для роботи з окремими класами.
Для зміни стилів:
-
Властивість
style
– об’єкт зі стилями, записаними верблюдячимРегістром. Читання і запис у неї має такий самий зміст, як і модифікація окремих властивостей в атрибуті"style"
. Щоб побачити, як застосовуєтьсяimportant
та інші рідкісні речі – на MDN описано перелік методів. -
Властивість
style.cssText
показує атрибут"style"
в цілому, весь рядок стилів.
Для зчитування вирішених стилів (з врахуванням всіх класів, після застосування всього CSS і обчислення остаточних значень):
- Метод
getComputedStyle(elem, [pseudo])
повертає об’єкт, подібний до властивостіstyle
, з вирішеними значеннями. Виключно для читання.
Коментарі
<code>
, для кількох рядків – обгорніть їх тегом<pre>
, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)