Pravila primene CSS-a i specifičnost selektora

U ovoj lekciji ćemo naučiti kako se određuje "prednost" na osnovu selektora, odnosno koja CSS formatiranja će na kraju biti primenjena na elemente. Naučićemo kako funkcioniše nasleđivanje CSS vrednosti, kako atributi nadjačavaju jedni druge na osnovu redosleda, kako da odredimo specifičnost selektora i kako se koristi !important pravilo u CSS-u.

Ako želimo da u potpunosti savladamo selektore, a to moramo za bilo kakvo ozbiljnije korišćenje CSS-a, ovo je lekcija koju ne smemo da zaobiđemo. Zavisno od toga kako ste čitali lekcije, neki selektori koje ovde pominjemo su vam možda još uvek nepoznati, ali kada ih naučite, vratite se još jednom na ovaj tekst i ponovo ga pročitajte. Toliko je važno.

O čemu se radi? Nećete verovati, sasvim je uobičajena situacija da se za isti HTML element zadaju konfliktni CSS atributi. To vrlo često može biti upravo isti atribut zadat na različitim mestima. Naravno, problem nastaje kada istom atributu na različitim mestima zadajemo različite vrednosti. Onda se pitamo - zašto je web čitač "poslušao" jednu, a ne drugu CSS deklaraciju?

Nasleđivanje

Prva stvar kojom ćemo se baviti je - šta se dešava sa formatiranjem kada se HTML elementi nalaze jedan unutar drugog? Možda ste do sada primetili da neki CSS atributi nasleđuju vrednost od nadređenog elementa. Ovo je uobičajeno za npr. boju slova, podešavanja fonta, poravananje i sl. dok na većinu neće imati nikakvog uticaja.

U stvari, bila bi prava katastrofa kada ovo ne bi funkcionisalo. Na primer - zamislite da želite da cela stranica ima plavu boju slova. Sasvim logično, zadaćete plavu boju za <body> element. Na kakvoj bismo se muci našli kada bi se desilo da pasusi unutar dokumenta "odbijaju" da prime plavu boju slova... Ili svaki <b> ili <i> element unutar pasusa? Onda bismo za sve morali da podešavamo jednu te istu plavu boju.

Kao što smo rekli, ovo ne važi za sve atribute. Ako recimo sekcija ima definisan okvir, pasusi unutar nje neće zbog toga poprimiti okvire.

Inače, pravilo nasleđivanja je prilično slabo - vrlo lako se "nadjača", prostim zadavanjem vrednosti atributa za podređeni elemenat. Da je nasleđivanje "stvar" u CSS-u, govori i činjenica da za većinu atributa postoji moguća vrednost inherit koja znači da se vrednost atributa preuzima od nadređenog elementa.

Primer - Nasleđivanje vrednosti CSS atributa

U ovom primeru ćemo demonstrirati nasleđivanje kao pravilo za definisanje izgleda elemenata. Zadaćemo selektore za body a onda i div i p elemente.


<style>
body {
  color: blue;  /* BODY, a samim tim i svi podređeni elementi će imati plavu boju slova */
}
div {
  color: red;   /* međutim DIV blok poništava nasleđivanje jer postavlja svoju boju teksta */
  background-color: #ffc;
  border: 1px solid black;  /* DIV blok ima border */
}
p {
  width: 80%;
  margin: 1em auto;
  border: inherit;   /* normalno podelementi ne nasleđuju border, ali ovde to baš zahtevamo */
}
</style>

<body>
  <h1>Naslov</h1>
  
  <div>
    Običan tekst u DIV bloku
    <p>Pasus sa nasleđenim okvirom</p>
  </div>
  
  <p>Pasus van DIV bloka nema border, pošto nema od čega da ga nasledi. Svi elementi imaju plav tekst, osim ako nije drugačije zadato.</p>

</body>

To izgleda ovako:

p2

U body elementu smo zadali plavu boju za tekst. Pošto je boja teksta nešto što se nasleđuje, svi podređeni elementi će imati tu istu boju. Međutim, čim nekom elementu zadamo drugu boju, ona odmah nadjačava ono što je nasleđeno. Tako i div blok ima crvenu boju teksta i sada njegovi podelementi nasleđuju tu boju. Dakle pogledajte boju teksta u pasusu unutar div bloka i pasusu koji je unutar body elementa.

Nije nam mnogo važno za današnju lekciju, ali možete videti kako funkcioniše nasleđivanje putem inherit vrednosti atributa. Pasusima smo zadali da nasleđuju border, pa će zbog toga imati onakav okvir kakav imaju njihovi neposredno nadređeni elementi.

Redosled atributa

Pogledajmo najpre situaciju u kojoj navodimo isti (ili konfliktni) atribut u okviru jednog selektora:

selektor { ... border: 1px solid black; border-bottom-width: 3px; ... }

Znači želimo da svi okviri oko elementa budu debljine 1px, pune linije i crni, s tim što samo za donju liniju želimo da bude debljine 3px. Umesto da zadajemo komplikovan border atribut, lakše je da prvo zadamo izgled svih okvira, a onda samo promenimo donji. Ovo je moguće jer kasnije naveden atribut nadjačava atribute koji su navedeni ispred njega.

Ovo takođe važi i kada navedemo isti selektor više puta. Ovo je sasvim moguće i nije greška, naravno, ako smo to namerno uradili. Ta dva (ili više) ista selektora će u web čitaču biti "spojeni" u jedan i tada važi pravilo vezano za deklaracije - ono što dolazi poslednje je "najjače".

selektor { border: 1px solid black; } ... selektor { border-bottom-width: 3px; }

Ovo je u stvari ista stvar kao prethodni primer, jedino što smo atribute smestili na dva odvojena mesta, samo pod istim selektorom.

Primer - Redosled atributa u CSS-u

Evo jednog malog primera, samo da pokažemo kako kasnije naveden atribut "prepisuje" prethodni.


<style>
div {
  border: 1px solid black;    /* DIV blok ima border... */
  border-bottom-width: 6px;   /* ...koji onda modifikujemo */
}
p {
  font-style: italic;
  color: red;           /* Zadajemo crvenu boju za pasuse */
}
section {
  /* ovde je jasno da redosled atributa može biti jako važan */
  border-bottom-width: 3px;
  border: 1px solid black;
}
p {
  color: blue;          /* Menjamo boju u pasusima */
}

</style>

<div>
  DIV blok sa borderom. Obratite pažnju na donju liniju!
</div>
  
<p>Da li pasus ima crvenu ili plavu boju teksta?</p>

<section>
  SECTION takođe ima border, ali on ne izgleda kao okvir DIV bloka.
</section>

Pogledajmo rezultat:

p3

Da vidimo šta se desilo - div bloku smo podesili border, a onda ga sledećom deklaracijom modifikovali. Kasnije navedena deklaracija je "nadjačala" prethodnu. Sa druge strane, za section element smo uradili sličnu stvar, samo što smo obrnuli redosled deklaracija. Tu vidimo da pošto smo prvo podesili donji border, a onda sve bordere, prva deklaracija biva poništena.

Osim toga, definišemo i izgled pasusa, ali na dva mesta. U prvom p selektoru podešavamo crvenu boju za tekst, ali je kasnije menjamo u plavu u drugom p selektoru. Pošto je plava boja kasnije navedena, to će na kraju i biti boja pasusa.

Lista klasa

Kad se već bavimo ovim stvarima, hajde da još razjasnimo šta se dešava kada u jednom HTML elementu navedemo listu klasa.


<style>
.prvo {
  font-weight: bold;
  color: red;           /* Zadajemo crvenu boju za klasu */
}
.drugo {
  background-color: #cff;
  color: blue;           /* Zadajemo plavu boju za klasu */
}
</style>

<p class="prvo">
  Pasus klase PRVO.
</p>

<p class="drugo">
  Pasus klase DRUGO.
</p>

<p class="prvo drugo">
  Pasus sa klasama PRVO i DRUGO.
</p>

<p class="drugo prvo">
  Pasus sa klasama DRUGO i PRVO.
</p>

Evo šta se dobija - možda malo neočekivano:

p4

Dakle, vidimo da redosled navođenja klasa u listi parametra class="" nije bitan. Važno je samo kakav je redosled deklaracija u CSS-u. Situacija je čista kada koristimo samo klasu "prvo" ili klasu "drugo". Međutim, kada se navedu obe klase, web čitač prosto ujedini deklaracije koje se odnose na obe klase i tada se desi da boja teksta iz klase "drugo" nadjača boju iz klase "prvo", samo zato što je ova klasa u CSS-u navedena kasnije.

Specifičnost selektora

Prvo što moramo da shvatimo je da se različiti selektori mogu odnositi na iste elemente. Specifičnost selektora znači da će u primeni CSS pravila, prednost imati specifičniji selektor. Šta ovo znači? Pogledajmo sledeća četiri osnovna selektora - svi su različiti, ali se takođe i svi odnose na prikazani div element. Jedino što su neki "jači" a neki "slabiji", upravo na osnovu specifičnosti:

* { odnosi se na svaki element } div { odnosi se na svaki DIV blok } .klasa { odnosi se na svaki element klase "klasa" } #ident { odnosi se na element identifikovan kao "ident" } ... <div id="ident" class="klasa">...</div>

Uporedite selektor * koji se odnosi baš na sve elemente i selektor div koji se odnosi samo na <div> blokove. Šta je od toga opštije, a šta specifičnije? Naravno, div selektor je specifičniji i on će nadjačati univerzalni * selektor.

Sa druge strane, selektor .klasa je još specifičniji od div selektora, pošto se odnosi na grupu elemenata kojima je dodeljena klasa "klasa". Međutim, selektor #ident se odnosi na jedan jedini element, identifikovan kao "ident", pa je kao takav još specifičniji. A ako bismo takvom elementu definisali style parametar? Pa, to što se zadaje kao inline CSS, očigledno bude "super-specifično", te tako nadjačava sve selektore.

Pravilo specifičnosti nadjačava i pravilo nasleđivanja i pravilo redosleda. Znači da se redosled deklaracija gleda tek ako su selektori iste "snage". Ne zaboravite, sve ovo važi samo za CSS atribute koji se ponavljaju pod različitim selektorima. Ako su svi selektori potpuno različiti (bez istih ili konfliktnih atributa), nije nas mnogo ni briga za specifičnost.

Ovo sve do sada deluje prilično jasno i jednostavno, zar ne? Međutim, pogledajte sada sledećih nekoliko CSS selektora:

div.klasa { odnosi se na svaki DIV blok klase "klasa" } h2::first-letter { odnosi se na prvo slovo svakog H2 naslova } a[target="_blank"] { odnosi se na svaki A link koji se otvara u novom prozoru } .klasa>p:first-child { odnosi se na onaj P pasus koji je prvi element unutar elementa klase "klasa" } #ident:hover { odnosi se na element identifikovan kao "ident" iznad koga je miš }

Šta je ovde više, a šta manje specifično? Faktori koji utiču na specifičnost se po "snazi" mogu podeliti na više nivoa. U te faktore spadaju osnovni selektori i neki načini zadavanja CSS-a.

Ono zbog čega nas boli glava, nalazi se na nivoima 1, 2 i 3. Srećom, pokazalo se da tu ipak nema velike mudrosti, specifičnost prosto možemo odrediti brojanjem koliko ima selektora na kom nivou. Verovatno je ilustrativnije da pogledate dijagram:

Kako se računa specifičnost selektora u CSS-u
Kako se računa specifičnost u CSS-u - predstavili smo sve "faktore" koji utiču na specifičnost, od najslabijeg do najjačeg. Osnovni selektori koji se prebrojavaju, navedeni su kao "brojači".

Pri tom, jedan selektor jačeg nivoa nadjačava bilo koji broj selektora svih slabijih nivoa. To znači da nam je za jačinu selektora najpre važan broj najjačih osnovnih selektora, pa tek ako je on jednak, gledamo broj osnovnih selektora prvog nižeg nivoa.

Takođe, treba da znamo da ni kombinatori selektora ne utiču na specifičnost. Ovde mislimo na "operatore" kojima definišemo određene odnose među selektorima, kao što su podređeni ili prateći elementi. Isto tako, videli smo da univerzalni selektor nema nikakav uticaj, pa ga i ne brojimo:

div.klasa vredi isto kao npr. div > .klasa div vredi isto kao npr. div > *

Hajde onda da skupimo "na gomilu" sve do sada nabrojane selektore, pa da ih (na osnovu ovoga što smo naučili) poređamo od najslabijeg do najjačeg:

* _______________________ 0|0|0 div _____________________ 1|0|0 h2::first-letter ________ 2|0|0 .klasa __________________ 0|1|0 div.klasa _______________ 1|1|0 a[target="_blank"] ______ 1|1|0 .klasa>p:first-child ____ 1|2|0 #ident __________________ 0|0|1 #ident:hover ____________ 0|1|1

Obratite pažnju na sledeće "kvake":

Primer - specifičnost selektora

Sa ovim primerom nećemo ići u detalje - prikazaćemo samo najosnovnije funkcionisanje pravila specifičnosti selektora. Ako ne čitate ove lekcije po redu, onda tek treba da se upoznajete sa različitim tipovima selektora, a do sada ste već verovatno dovoljno zbunjeni. Za ovaj primer je dovoljno da ste savladali HTML selektore, selektore klase i ID selektore.


<style>
#ident {
  background-color: #dfc;
  color: blue;
}
.klasa {
  font-weight: bold;
  border-color: green;
  color: green;
}
div {
  padding: 0.5em;
  border: 3px solid black;
  color: red;
}
</style>

<div>
  Običan DIV blok.
</div>
<div class="klasa">
  DIV blok klase "klasa".
</div>
<div id="ident" class="klasa">
  DIV blok na koji utiču svi navedeni selektori...
</div>

Šta dobijamo?

p5

U CSS-u smo namerno naveli selektore od najspecifičnijeg ID selektora do najmanje specifičnog HTML selektora. Time smo pokazali kako specifičnost nadjačava redosled.

I zaista - na običan <div> blok deluje samo ono što je navedeno u div selektoru. Međutim, na <div> blok klase "klasa" se odnose dva selektora - i div i .klasa. Pošto je selektor klase specifičniji od HTML selektora, svaki atribut koji se "preklapa" (color i border) će biti poništen i preuzet iz .klasa. Tako boja teksta neće više biti crvena, nego zelena, niti će boja okvira biti crna, već isto zelena.

Isto važi i za poslednji <div> blok. Osim što je to <div> blok, nego je i klase "klasa", a još preko toga i identifikovan kao "ident". Znači na njega se odnose sva tri selektora. Međutim, kod konfliktnih atributa, sada imamo situaciju da .klasa nadjačava div, a #ident nadjačava .klasu. Tako poslednji <div> blok dobija plavu boju teksta.

Primetite da atributi koji nisu u konfliktu, "prolaze" bez problema - npr. debljina i tip linije, kao i padding, preuzeti su iz div selektora, boja okvira i podebljana slova iz selektora .klasa, a boju pozadine dodaje selektor #ident.

!important pravilo

Bilo koju deklaraciju unutar selektora možemo "pojačati", tako da nadjača adekvatne atribute iz svakog drugog selektora. Ovo postižemo tako što na kraju deklaracije navodimo pravilo !important.

Pravilo !important ima prioritet nad svim ostalim selektorima, koliko god da su visoke specifičnosti. Jače je čak i od inline CSS-a. Zbog toga morate da ga koristite jako pažljivo. Ovo je praktično poslednje sredstvo.

div { border: 1px solid black !important; border-bottom-width: 3px; <-- ništa od bottom bordera } #ident { border-color: red; <-- ni ovo ne važi } ... <div id="ident" style="border-width:2px;"></div> <-- čak ni style ne menja border

Koristimo ga kad smo u zaista nezavidnoj situaciji. Npr. ako radimo sa stranicom formatiranom po šablonu koji je pravilo neko treće lice. U tom slučaju je CSS praktično "crna kutija", tj. trebalo bi nam puno vremena da ga analiziramo i promenimo. To isto važi i ako koristimo tuđe plug-inove, dodatke za web aplikaciju ili ako se CSS atributi elementa definišu programski, preko JavaScripta.

Isto tako, moglo bi i nama da se desi da je CSS posle nekog vremena postao toliko komplikovan da svaka promena ima nepredviđene posledice na gomilu drugih elemenata. Tada nas !important može spasiti ali imajte na umu - pravilo !important će samo doprineti haosu u vašem CSS-u.

Jedini način da se već postojeće !important pravilo poništi, je uvođenje nove !important deklaracije koja se odnosi na isti elemenat, za isti taj atribut. Ova deklaracija obavezno mora da se u CSS-u nađe negde posle postojeće deklaracije (koju želimo da poništimo).

Primer - !important pravilo

Pogledajmo sada kako izgleda primer za navođenje !important pravila.


<style>
div {
  border: 1px solid black !important;
  border-bottom-width: 3px;
}
#ident {
  border-color: red;
}
</style>

<div id="ident" style="border-width:2px;">Kakav border ima ovaj DIV blok?</div>

Evo rezultata:

p1

I, eto ga - kako smo zakucali border sa pravilom !important, još u div selektoru, ništa nije uspelo da ga promeni - ni sledeća deklaracija u istom selektoru, ni specifičniji ID selektor #ident, pa čak ni inline CSS u samom <div> elementu.

  1. Standardista, CSS Specificity
  2. Mozilla Developer Network, Specificity
  3. CSS-Tricks, Specifics on CSS Specificity
  4. D.S. McFarland (2013): CSS3 The Missing Manual, O'Reilly, Sebastopol
Svi elementi sajta Web'n'Study, osim onih za koje je navedeno da su u javnom vlasništvu, vlasništvo su autora i ne smeju se koristiti, u celosti ili delimično bez pismenog odobrenja autora. To uključuje tekstove, slike, ilustracije, animacije, prateći grafički materijal i programski kod.
Ovaj sajt koristi tehnologiju kolačića (cookies). Detaljnije o tome možete pročitati u tekstu o našoj politici privatnosti.