Kuinka CSS ja JavaScript tukevat toisiaan
Webin alkuajoista lähtien kehittäjät ovat hyödyntäneet JavaScriptiä ja CSS:ää parivaljakkona käytännön ongelmien ratkaisemissa. Tilanteet kuten elementtien piilottaminen, tyylien vaihtaminen lennosta, selaintuen kiertäminen sekä käyttöliittymän reaktiivisuuden parantaminen ovat yleisiä esimerkkejä. Näitä ongelmia ratkottiin pitkään selainkohtaisilla kikoilla, epästandardeilla ratkaisuilla ja hajanaisella koodilla. Vasta 2020-luvulla modernit API:t ja selkeät arkkitehtuurimallit – kuten classList, CSS.supports() ja progressiivinen parantaminen – ovat tuoneet siisteyttä, skaalautuvuutta ja parhaita käytäntöjä CSS:n ja JavaScriptin yhteiskäyttöön. Seuraavat esimerkit havainnollistavat miten näiden teknologioiden yhteiskäyttö on kehittynyt, mitä menneitä ratkaisumalleja kannattaa välttää ja mitä lähestymistapoja suosia.
Ongelma: Selaimien tuki ominaisuuksille on vaihtelevaa
Selaimet ovat tulkinneet CSS:ää eri tavoin, mikä on tehnyt ulkoasun hallinnasta haastavaa. Aluksi ongelmaa yritettiin ratkaista tunnistamalla selain JavaScriptin navigator-olion avulla. Tämän perusteella eri selaimille, kuten Internet Explorerille ja Gecko-selaimille, ladattiin niille räätälöidyt tyylitiedostot. Tämä menetelmä oli kuitenkin altis virheille, sillä selainten UA-tunnistetiedot saattoivat olla epätarkkoja, muuttua tai jopa puuttua kokonaan.
...
<title>CSS tyylitiedoston liittäminen JavaScriptin avulla</title>
<script type="text/javascript">
function lisaaTyyli(sTyylitiedosto) {
/*
xhtml + xml dokumenteissa tulisi hyödyntää
DOM:n mukaisia menetelmiä document.write-menetelmän sijaan
*/
if (document.getElementById) {
if(document.createElementNS) {
var l=document.createElementNS("http://www.w3.org/1999/xhtml","link");
l.setAttribute("rel", "stylesheet");
l.setAttribute("type", "text/css");
l.setAttribute("href", sTyylitiedosto);
l.setAttribute("media", "screen");
document.getElementsByTagName("head")[0].appendChild(l);
}
}
//else {...}
}
var sTyyli;
if (navigator.appName == "MSIE"){
sTyyli = "/tyylit/ie.css";
}
else {
var sTyyli = "/tyylit/gecko.css";
}
lisaaTyyli(sTyyli);
</script>
</head>
...
Nykyään tarkistetaan suoraan, tukeeko selain tarvittavaa CSS-ominaisuutta. CSS:n @supports ja JavaScriptin CSS.supports() mahdollistavat tämän. Esimerkiksi, jos CSS Grid on tuettu, käytetään sitä. Muussa tapauksessa tarjotaan vaihtoehtoinen fallback-ratkaisu:
if (CSS.supports("display", "grid")) {
// tee jotain, esimerkiksi lisää modern-layout class elementille
element.classList.add("modern-layout");
}
else {
// fallback-ratkaisu
}
Tämä lähestymistapa on tarkempi ja kestää paremmin aikaa. Uusien selainten ei tarvitse olla ennakkoon tunnettuja, riittää että ne tukevat tarvittavaa ominaisuutta.
Ongelma: Elementtien tyylin muuttaminen tilanteen mukaan
Aikaisemmin CSS-tyylejä hallittiin suoraan DOM:n style-ominaisuuden kautta.
JavaScriptillä haettiin elementtejä esimerkiksi getElementsByTagName- tai getElementById-metodilla ja tyylejä muutettiin suoraan:
document.getElementById("elementti").style.display = "none";
Tämä teki koodista hankalasti ylläpidettävää, koska tyyli ja toiminnallisuus kietoutuivat yhteen. Lisäksi tyylien muokkaus vaati koodin sijoittamista sivun loppuun, jotta elementit olivat jo ladattuja.
Nykyisin suositaan classList-APIa, jolloin varsinainen tyyli säilyy CSS-tiedostossa. Tämä erottaa tyylin ja toiminnallisuuden toisistaan, parantaa luettavuutta ja helpottaa ylläpitoa. classList-API:n avulla elementille lisätään tai poistetaan valmiiksi määriteltyjä tyyliluokkia:
// css-tiedostossa:
.piilotettu {
display: none;
}
// ja js-koodissa
document.getElementById("elementti").classList.add("piilotettu");
Ongelma: Dynaamisten ominaisuuksien lisääminen helposti
Kun verkkosivuille haluttiin lisätä reaktiivisuutta, kuten elementtien piilotusta tai tyylien muuttamista käyttäjän toiminnan perusteella, jopa yksinkertaisetkin toiminnot johtivat helposti kaoottiseen koodiin.
Varhainen ratkaisu oli lisätä toiminnallisuus suoraan HTML:ään:
<ul>
<li onclick="this.parentNode.style.display='none';">Piilota listaus </li>
...
Tämä ratkaisu oli toimiva, mutta HTML-koodi oli vaikeasti ylläpidettävää ja testattavaa. Samalla rikkoutui sisällön, ulkoasun ja toiminnallisuuden erottamisen käytäntö.
Microsoftin dHTML Behaviours tarjosi vaihtoehdon: CSS:n behavior-määrittelyllä viitattiin JavaScript-komponenttiin. Ratkaisu piti HTML-koodin siistinä, mutta se toimi ainoastaan Internet Explorerissa eikä tukenut nykyaikaisia standardeja tapahtumamalleja.
Myöhemmät kolmannen osapuolen kirjastot, kuten CSS Behaviour, mahdollistivat toiminnallisuuden lisäämisen JavaScript-tiedostoon CSS-valitsimien avulla.:
/*tiedosto: behaviours.js */
var piilotaLista = {
'ul li#piilota-lista' : function(e){
e.onclick = function(){
this.parentNode.style.display='none'
}
}
};
Behaviour.register(piilotaLista);
Tämä paransi koodin luettavuutta, mutta toi riippuvuuden erilliseen koodipohjaan.
Nykyisin suositaan natiiveja tapahtumankuuntelijoita. Myös rakenne, tyyli ja toiminnallisuus pyritään pitämään erillään joko tiedosto- tai komponenttitasolla:
document.getElementById("piilota-lista").addEventListener("click", () => {
document.getElementById("piilota-lista").parentNode.style.display = "none";
});
Tämä on standardinmukaista, toimii useimmissa selaimissa ilman polyfillejä, ja on helposti testattavaa ja ylläpidettävää.