Praca z CSS
Ze stylowaniem elementów możemy działać na kilka sposobów.
Właściwość style
Najczęściej spotykanym sposobem jest skorzystanie z właściwości style, którą możemy użyć do ustawiania i odczytu stylowania:
const btn = document.querySelector(".button");
btn.addEventListener("click", function() {
this.style.backgroundColor = "#4BA2EA";
this.style.fontSize = "1.6rem";
this.style.borderRadius = "3rem";
this.style.color = "#F7F781";
});
Jeżeli właściwość składa się z jednego słowa, to zapisujemy ją tak jak w css. Jeżeli właściwość składa się z kilku słów oddzielonych myślnikiem, wtedy dla takiej właściwości musimy zastosować zapis CamelCase. Możemy tutaj też zastosować zapis znany z obiektów:
//css
font-size : 1rem;
//js
el.style.fontSize = "1rem";
el.style['font-size'] = "1rem";
//css
background : linear-gradient(#fff, #ddd);
//js
el.style.background = "linear-gradient(#fff, #ddd)";
el.style['background'] = "linear-gradient(#fff, #ddd)";
//css
background-color : rgba(255,255,255,0.5);
//js
el.style.backgroundColor = "rgba(255,255,255,0.5)";
el.style['background-color'] = "rgba(255,255,255,0.5)";
//css
border-width : 2px
//js
el.style.borderWidth = "2px"
el.style['border-width'] = "2px";
Podobne zasady tyczą się odczytu styli, do którego używamy tej samej właściwości:
<div class="module">
<h3 class="module-title">Przykładowa nazwa artykułu</h3>
<div class="module-content" style="display:none">
Lorem ipsum dolor sit amet...
</div>
<button type="button" class="button module-button" id="testShowContent">Pokaż treść</button>
</div>
const btn = document.querySelector(".module-button");
const textCnt = btn.previousElementSibling;
btn.addEventListener("click", function() {
if (textCnt.style.display === "none") {
textCnt.style.display = "block";
} else {
textCnt.style.display = "none";
}
});
Przykładowa nazwa artykułu
Zauważ, że początkowe ukrycie treści uzyskałem przez display:none zapisane w atrybucie style danego elementu. Wrócimy do tej sprawy poniżej.
setProperty() i getPropertyValue()
Właściwość style udostępnia nam też dodatkowe metody, wśród których 2 kluczowe to:
style.setProperty(propertyName, value, priority*) - służy do ustawiania stylowania. Ostatni opcjonalny parametr priority służy do ewentualnego dodania do danych styli deklaracji !important. Najczęściej jest pomijany.
style.getPropertyValue(property) - służy do pobierania stylowania.
const btn = document.querySelector(".button");
btn.addEventListener("click", function() {
if (this.style.getPropertyValue("font-size") !== "1.5rem") {
this.style.setProperty("background-color", "#4BA2EA");
this.style.setProperty("font-size", "1.5rem");
this.style.setProperty("border-radius", "3rem");
} else {
this.style.setProperty("background-color", "");
this.style.setProperty("font-size", "");
this.style.setProperty("border-radius", "");
}
});
getComputedStyle()
Jeżeli ustawiamy style za pomocą powyższych sposobów, są one wstawiane inline w atrybucie style danego elementu.
Żeby to sprawdzić, odśwież stronę, przejdź do debugera i zbadaj przycisk z pierwszego przykładu. Kliknij na niego i zobacz jak doklejane są style.

Podobna zasada tyczy się odczytu. Jeżeli dany element nie ma ustawionych styli inline (wpisanych z palca, lub za pomocą JavaScript), to nie jesteśmy w stanie ich pobrać:
const btn = document.querySelector("#testInline");
btn.addEventListener("click", function() {
console.log(this.style['font-size']); //wypisze ""
console.log(this.style.backgroundColor); //wypisze ""
this.style['font-size'] = "1.5rem"; //ustawiamy style inline
this.style.backgroundColor = "#4BA2EA"; //to samo z kolorem tła
console.log(this.style['font-size']); //wypisze "1.5rem"
console.log(this.style.backgroundColor); //wypisze "#4BA2EA"
});
Bardzo często będziemy chcieli jednak sprawdzić lub pobrać style, które są zadeklarowane w arkuszach stylów a nie inline w kodzie HTML. Przykładowo powyższy przycisk ma swój wygląd (np. background), który nadała mu klasa .button z pliku ze stylami.
Żeby pobrać takie stylowanie musimy skorzystać z metody getComputedStyle(elem, pseudoEl*) obiektu window, która zwraca przeliczone przez przeglądarkę stylowanie. Pierwszy parametr tej metody określa badany element, drugi - opcjonalny pozwala pobierać style dla pseudo elementów. Jeżeli nie chcemy badać pseudo elementów, wtedy używamy null, lub go pomijamy:
const btn = document.querySelector(".button");
const styleComputed = getComputedStyle(textCnt);
console.log( styleComputed.getPropertyValue("height") );
console.log( styleComputed.width );
console.log( styleComputed['background-color'] );
Gdy sprawdzisz w konsoli wynik powyższego skryptu, zorientujesz się, że dane zwracane przez omawianą metodę różnią się od tego, co zostało podane w CSS.
I tak w stylach kolor przycisku ma #f15c5c, a zwracany jest rgb(166, 14, 14). Podobnie z rozmiarem czcionki. W CSS jest on zapisany w jednostce rem, a zwracany jest w px. W tym ostatnim przypadku żeby uzyskać rezultat w rem trzeba wynik przeliczyć przez zwrócony rozmiar dla elementu html.
Wartość style pobrana za pomocą getComputedStyle jest właściwością tylko do odczytu. Próba ustawienia za jej pomocą nowych stylów zakończy się błędem:
Mając teorię za sobą, poprawmy wcześniejszy przykład i usuńmy z niego style inline:
<div class="module">
<h3 class="module-title">Przykładowa nazwa artykułu</h3>
<div class="module-content"> <!-- usuneliśmy atrybut style -->
Lorem ipsum dolor sit amet...
</div>
<button type="button" class="button module-button" id="testShowContent">Pokaż treść</button>
</div>
const btn = document.querySelector(".button");
const textCnt = btn.previousElementSibling;
btn.addEventListener("click", function() {
btn.addEventListener("click", function() {
const styleComputed = getComputedStyle(textCnt);
if (styleComputed.display === "none") {
textCnt.style.display = "inline-block";
} else {
textCnt.style.display = "none";
}
});
});
Przykładowa nazwa artykułu
Aby pobrać style dla pseudo elementu musimy skorzystać z drugiego parametru tej metody:
const module = document.querySelector('.module');
const styleComputed = getComputedStyle(textCnt, ':before');
console.log(styleComputed.backgroundColor);
classList
W powyższych przykładach ustawialiśmy właściwości css za pomocą JavaScript. Niema co ukrywać - w dzisiejszych czasach starsze zasady tworzenia stron się nieco rozmywają. Jedna z takich zasad mówiła o oddzieleniu warstw od siebie. HTML to treść dokumentu, JavaScript odpowiada za manipulowanie taką treścią i dodawanie interakcji. Za wygląd odpowiedzialny jest CSS.
Właśnie dlatego w wielu przypadkach ustawianie wyglądu powinno się odbywać za pomocą CSS - a dokładnie klas w css, a JavaScript powinien tylko odpowiednio manipulować tymi klasami - dodawać je, odejmować itp. Dzięki temu jeżeli w przyszłości najdzie konieczność np. zmiany sposobu zmiany danego przycisku, zmienisz tylko odpowiednie klasy w CSS, a JavaScript zostawisz w spokoju.
Aby zarządzać klasami css danego elementu użyjemy właściwości classList, która udostępnia nam kilka metod:
add("nazwa-klasy") | dodawanie klasy |
---|---|
remove("nazwa-klasy") | usuwanie klasy |
toggle("nazwa-klasy") | przełączanie (jak nie ma to dodaje, jak jest to usuwa) klasy |
contains("nazwa-klasy") | sprawdza czy element ma taką klasę |
const btn = document.querySelector('.btn');
btn.classList; //zwraca pseudo tablicę z nazwami klas
btn.classList.add('btn'); //dodaję klasę .btn
btn.classList.remove('btn'); //usuwam klasę .btn
btn.classList.toggle('btn'); //przełączam (dodaję lub usuwam) klasę .btn
btn.classList.contains('btn'); //sprawdzam czy dany element ma klasę .btn
Przykładowo mamy przycisk, który działa jak przełącznik. Po kliknięciu włączamy lub wyłączamy tryb edytowania jakiś pól:
const btn = document.querySelector('.btn-test-edit');
btn.addEventListener('click', function() {
this.classList.toggle('editable'); //przełączam klasę buttonowi
const inputs = this.form.querySelectorAll('input, textarea');
if (this.classList.contains('editable')) { //sprawdzam czy button ma klasę
//włącz tryb edytowania
this.form.classList.add("edited");
this.innerText = "Zakończ";
for (const inp of inputs) {
inp.disabled = false;
}
} else {
//wyłącz tryb edytowania
this.form.classList.remove("edited");
this.innerText = "Edytuj";
for (const inp of inputs) {
inp.disabled = true;
}
}
});
className
Właściwość className zwraca nam jako tekst wszystkie klasy jakie posiada dany element:
const btn = document.querySelector('.btn');
console.log(btn.className); //btn btn-primary
W czasach gdy wsparcie dla classList było marginalne, była to główna właściwość, która pozwalała zarządzać klasami. Każdy taki manewr wymagał dodatkowego kodu:
const btn = document.querySelector('.btn');
const classText = "nazwaKlasy";
//add
btn.className += " "+classText;
//remove
const classText = classText;
const regPattern = new RegExp('(\\s|^)'+classText+'(\\s|$)');
btn.className = btn.className.replace(regPattern, '');
//contains
if (btn.className.indexOf('btn')!==-1) {
console.log('Przycisk ma klasę btn');
}
//toggle
if (btn.className.indexOf('btn')!==-1) {
btn.className += " "+classText;
} else {
const regPattern = new RegExp('(\\s|^)'+classText+'(\\s|$)');
btn.className = btn.className.replace(regPattern, '');
}
Ogólnie nie polecam stosować tej właściwości do manipulacji klasami, a tylko do ewentualnego odczytu nazwy klasy.
Zmienne w CSS
CSS także doczekał się swoich zmiennych. Ich podstawowe użycie ma postać:
:root {
--color : red; /* zmienne deklarujemy poprzedzając ich nazwę -- */
}
.module-error {
border:1px solid var(--color); /* a używamy ze słowem var */
box-shadow: 0 1px 3px var(--color);
}
.module-error-title {
color: var(--color);
}
Zmienne w CSS zachowują się mniej więcej tak jak zmienne w JS - czyli ich zasięg jest dostępny dla coraz bardziej zagnieżdżonych elementów (tak jak zmienne w JS są dostępne dla coraz bardziej zagnieżdżonych funkcji).
W powyższym kodzie zadeklarowałem pojedynczą zmienną w elemencie :root. Element taki dla stron jest równoznaczny z html (można pisać tak i tak). Dzięki temu zmienna taka jest dostępna dla całego dokumentu.
Takie zmienne możemy też deklarować w każdy elemencie z osobna. Dzięki temu dostępne są one tylko w tym elemencie (i dla jego dzieci):
.header {
--font-size: 14px; /* zmienne dostępne tylko dla .header i jego dzieci */
--color1: #222;
--color2: #000;
--color-link : #fff;
background: linear-gradient(var(--color1), var(--color2));
}
.header a {
color: var(--color-link); /* ok */
}
.element {
/* jeżeli ten element nie jest w header to nie ma dostępu do powyższych zmiennych */
}
Zmienne CSS dają olbrzymie możliwości ich manipulacji. Możemy zmieniać ich wartość za pomocą zdarzeń :hover, :focus (i podobnych), możemy je zmieniać w kolejnych deklaracjach @media, a także za pomocą Javascriptu.
.element {
--color: red;
transition: 0.5s;
background: var(--color);
}
.element:hover {
--color : blue;
}
.element:focus {
--color : pink;
}
@media (max-width:500px) {
.element {
--color : yellow;
}
}
const elem = document.querySelector('.element');
elem.style.setProperty("--color", "black");
Poniżej zamieszczam kilka przykładów jak to można wykorzystać w praktyce.
Przykład 1 - zmiana koloru
W pierwszym przykładzie za pomocą inputa typu color zmieniamy właściwości elementu:
<div class="element">
Zmień kolor i najedź kursorem
</div>
<input type="color" class="color">
.element {
--color : red;
border: 2px solid var(--color);
color: var(--color);
padding: 30px;
font-weight: bold;
text-transform: uppercase;
font-family: sans-serif;
position: relative;
background: #eee;
transition: 0.5s;
}
.element:hover {
background-color: var(--color);
color: #fff;
}
const element = document.querySelector('.element');
const color = document.querySelector('.color');
color.addEventListener('change', function() {
element.style.setProperty('--color', this.value)
})
Przykład 2 - losowa pozycja
<button type="button" class="button-move">
Zamknij reklamę
</button>
.button-move {
--ml : 0px;
padding:0.5rem 1rem;
transform: translate(var(--ml));
transition: 0.3s;
}
const element = document.querySelector('.button-move');
setInterval(function() {
element.style.setProperty('--ml', Math.random() * 300 + "px");
}, 300)
Przykład 3 - zmiana po najechaniu
W kolejnym przykładzie pobieramy pozycję kursora na elemencie i ustawiamy mu pozycję pseudoelementu z gradientem. Dodatkowo na hover zmieniamy jego wielkość:
<button type="button" class="button-test">
<span>KLIK</span>
</button>
.button-test {
--size: 0;
--left: 0;
--top: 0;
border: 0;
padding: 20px 70px;
border-radius: 50px;
text-transform: uppercase;
font-family: sans-serif;
color: #fff;
overflow: hidden;
position: relative;
background-color: tomato;
}
.button-test span {
position: relative;
z-index: 1;
}
.button-test:hover { /* wielkość gradientu zwiększam na hover */
--size: 400px;
}
.button-test:after {
content: '';
position: absolute;
left: var(--left);
top: var(--top);
width: var(--size);
height: var(--size);
background: radial-gradient(circle closest-side, #4405f7, tomato 60%);
background-repeat: no-repeat;
transform: translate(-50%, -50%);
transition: 0.3s width, 0.3s height;
z-index: 0;
}
const element = document.querySelector('.button-test');
element.addEventListener('mousemove', function(e) {
this.style.setProperty('--left', e.pageX - this.offsetLeft + "px");
this.style.setProperty('--top', e.pageY - this.offsetTop + "px");
})
Przykład 4 - zmiana pozycji elementów
Ostatni przykład możesz zobaczyć na oddzielnej stronie. To jeden z moich eksperymentów odnośnie animacji w CSS. Tutaj także wykorzystałem powyższe techniki do animowania elementów.