[Alku]
Testaa CSS-oppaan navigoinnin toimivuutta!
 
 Etsi sivuiltani: [Apua]

Ongelmatilanteiden ratkaisut

Koska DHTML/DOM1 toimii eri asteisesti selaimissa, mielestäni DHTML/DOM:iin perustuvat valikot tulisi suunnitella siten, että sivut toimivat myös sellaisissa selaimissa, jotka eivät tue DHTML/DOM-skriptejä. Sivujen olisi syytä toimia myös tilanteissa, joissa JavaScript on poissa päältä. Olen havainnut tilanteita, joissa sivujen navigointia ei ole olemassa ilman sopivaa JavaScript-tukea. Syynä tähän on kaksi mahdollisuutta. Joko linkit luodaan "lennosta" JavaSript-koodauksella tai linkkien oletusarvo on se, että ne on piilotettu (nillä on oletuksena joko ominaisuus visibility:hidden or display:none). Pidän kumpaakin edellä mainittua ratkaisua huonona. Linkit saisi näkyviin, jos piilotus toteutettaisiin niin, että sivulle saavuttaessa kaikki valikot piilotetaan BODY-elementille annetun onload-attribuutin avulla. Tässä tapauksessa linkit olivat näkyvillä ilman JavaScript-tukea. Tämä ratkaisu ei kuitenkaan toimi, sillä dynaamisissa valikoissa linkit menevät muun sisällön ja mahdollisesti myös osittain tai kokonaan toistensa päälle.

Mielestäni parempia ratkaisuja ovat seuraavat:

  1. Päälinkit toimivat kaikissa selaimissa ja niiden kautta pääsee sivuille, joissa on jatkolinkit. Tässä ratkaisussa dynaamiset valikot ovat vain alivalikkoja. Tässä tapauksessa on epäolennaista luodaanko alivalikkojen linkit JavaScript-koodauksella vai ovatko linkit aina olemassa itse sivulla, mutta JavaScript piilottaa/paljastaa ne tarvittaessa. Sekään ei haittaa, vaikka osa linkeistä olisi piilotettuna, kun JavaScript on pois päältä.

    Selainkohtaisia huomautuksia:

    1. Ei ole mitenkään mahdollista ratkaista kaikkia JavaScript koodaukseen tai servereihin liittyviä ongelmia, mutta jos esitysasu ei ole riippuvainen siitä, onko JavaScript-tuki päällä vai pois päältä sivuilla vierailija ei ihmettele, miksi sivun ulkoasu muuttui äkisti. Tässä mielessä tämä vaihtoehto on järkevin.

  2. Valikoihin liittyvät LINK elementit tuotetaan skripteillä. CSS voidaan helposti räätälöidä eri selaimille sopivaksi. Alla on joitakin esimerkkejä mahdollista toteutustavoista:

    <script language="JavaScript" type="text/javascript">
    <!--
    function linkStyle(sStyleFile,sStyleMedia)
    {document.writeln('\<lin'+'k\ rel=\"stylesheet\"\ type=\"text\/css\"\
    href=\"..\/Css\/'+sStyleFile+'\"\ media=\"'+sStyleMedia+'\"\ \/\>');}
    /* Koodin lyhentäminen funktion avulla. Jatkossa tiedostonimi ja mediatyyppi annetaan funktion argumentteina. */

    if (document.layers)
    {linkStyle("layersNetscape4.css","screen");}
    /* Ehto Netscape 4.x -selaimille, jotka eivät tue DOM1:tä, mutta tukevat selainkohtaista DHTML:ää. Netscape 4.x -selaimille attribuutille media ei saa antaa yhtä useampaa arvoa (esim. screen,print). */

    else { /* CSS muille selaimille. Netscape 4.x -selaimilla on vaikeuksia monimutkaisten ehtolauseiden kanssa. On paras antaa sille yksinkertaisimmat mahdolliset ehdot. */
    if (document.getElementById) /* Tämä testaa tukeeko selain erästä DOM1:een kuuluvaa metodia. Ehto koskee kaikkia sellaisia selaimia, jotka tukevat käytetyssä skriptissä DOM1-jaksoa (Windows-käyttöjärjestelmässä sitä tukevat MS IE 5.0+, Opera 4.x+, Netscape 6.x+ ja vastaavat Mozilla -selaimet). */
    {linkStyle("layers.css","screen,projection");} /* Opera tarvitsee mediatyypin projection. Tulevaisuuden PDA-selaimet voivat tarvita myös handheld. */

    else if (document.all)
    {linkStyle("layers.css","screen");}
    /* Ehto kaikille sellaisille MS IE -selaimille, jotka eivät tue DOM1:tä, mutta tukevat selainkohtaista DHTML:ää. Koska käytetty CSS-tiedosto on sama kuin edellisessä testissä, ehto olisi voitu yhdistää edelliseen testiin käyttämällä muotoa (document.getElementById) || (document.all).*/

    else if (!(document.layers) && !(document.all) && !(document.getElementById)) {linkStyle("noLayers.css","screen");} /* Tuntemattomille selaimille, jotka eivät tue selainkohtaista DHTML:ää eivätkä standardia DOM1:tä. Jos ne tukevat CSS:ää, ne saavat staattiset, kerroksettomat linkkitaulukot. Tässä tapauksessa ehdot ovat tarpeettomia, mutta tämä on esimerkki poissulkevien ehtojen käytöstä. */

    else {} /* Lohkon päätös. Tämän kohdan voi jättää pois, mutta on hyvä tapa päättää ehdot tällä tavoin. */
    }
    //-->
    </script>
    <script language="JavaScript" type="text/javascript">
    <!--
    /* Tässä mallissa useimmat selaimet on tunnistettu selainkohtaisiin tietoihin perustuen. */

    function linkStyle(sStyleFile,sStyleMedia)
    {document.writeln('\<lin'+'k\ rel=\"stylesheet\"\ type=\"text\/css\"\
    href=\"..\/Css\/'+sStyleFile+'\"\ media=\"'+sStyleMedia+'\"\ \/\>');}


    var IE4, OP, Gecko; /* On hyvä käytäntö listata yleismuuttujat. */

    IE4 =(((navigator.userAgent.indexOf('MSIE')!=-1) || (navigator.userAgent.indexOf('Internet Explorer')!=-1)) && (navigator.userAgent.indexOf('Mozilla\/4.')!=-1)); /* Koska uudemmat MS IE -selaimet esittäytyvät myös Mozilla/4.0 yhteensopivina tämä tunnistus soveltuu myös uudemmille selaimille. Jotkut MS IE -selaimet käyttävät MSIE asemesta Internet Explorer tunnistusjonoa. */
    OPua = (navigator.userAgent.indexOf('Opera')!=-1); /* Opera-selainten yleistunnistus. */
    op_pos=nua.indexOf('Opera'); /* Hae tietty merkkijono. */
    OPnu=nua.substr((op_pos+6),1);
    OPnu2=nua.substr((op_pos+7),1);
    /* Ensiksi määritellään, mistä kohtaa numeeristen arvojen laskeminen aloitetaan tietyn merkkijonon jälkeen. Numero 1 kertoo kuinka monia digitaaleja käytetään. Joissakin Opera 5.x+ Symbian OS (EPOC) selaimien esittäytymisjonoissa versionumeron edellä voi olla kirjain v, jolloin tarvitaan muotoa (op_pos+7),1. */
    OP=(OPua && ((OPnu>=4) || (OPnu2>=5))); /* Tunnistus Opera 4.x or uudemmille selaimille. Tässä tapauksessa numeerisia arvoja vertaillaan keskenään edellä olleiden muuttujien (OPnu ja Opnu2) avulla. Opera-selaimiamia voitaisiin tunnistaa myös käyttämällä ((navigator.userAgent.indexOf('Opera 5')!=-1) || (navigator.userAgent.indexOf('Opera\/5')!=-1) || (navigator.userAgent.indexOf('Opera\/v5')!=-1) || (navigator.userAgent.indexOf('Opera v5')!=-1)) kaltaisia tunnistusjonoja. Jos esim. Opera 5.12 (Windows) toimisi esittäytymismoodissa Esittäydy: Opera (engl. Identify as Opera) se antaisi tunnistusjonon Opera/5.12 ja muissa esittäytymismoodeissa se antaisi jonon Opera 5.12. Koska dynaamiset valikot toimivat kunnolla vasta 5.x lähtien Opera 4.x olisi mielekästä jättää huomioimatta. */
    Gecko = (navigator.product == ('Gecko')); /* Kaikille Mozilla Gecko perusteisille selaimille tarkoitettu tunnistus. Voidaan käyttää myös saman tyyppistä tunnistusta kuin edellä MS IE selaimille (navigator.userAgent.indexOf('Gecko')!=-1). */

    if (document.layers)
    {linkStyle("layersNetscape4.css","screen");}
    /* Netscape 4.x -selainten dynaamisten valikoiden CSS. Netscape 4.x -selainten tunnistus luomalla tunnistusjonoa koskeva muuttuja ei toimi. */

    else {
    if(IE4||OP)
    {linkStyle("layers.css","screen,projection");}
    /* Dynaamisten valikoiden CSS MS IE 4.0+ ja Opera 4.x+ -selaimille. */

    else if (Gecko)
    {linkStyle("layersGecko.css","screen");}
    /* Kokonaan oma tai täydennetty dynaamisten valikoiden CSS Netscape 6.x+/Mozilla -selaimille. Oman CSS-tiedoston tarve on hyvin pieni. Mikäli omaa CSS:ää ei tarvita, tunnistaminen on mahdollista yhdistää edelliseen ehtoon kirjoittamalla se muotoon (IE4||OP||Gecko). */

    else {}
    //-->
    </script>
    <script language="JavaScript" type="text/javascript">
    <!--
    /* Metodi- ja selaintunnistuksen yhdistelmä. Kun tunnistus perustuu tuettuihin piirteisiin saadaan mahdollisimman laaja tuntemattomien selainten kattavuus. Ainakin ensisijainen tunnistus pitäisi perustua metodien tukemiseen. Vain osa tunnistuksesta tulisi perustua selaimen nimeen tai muihin selainkohtaisiin tietoihin. */

    function linkStyle(sStyleFile,sStyleMedia)
    {document.writeln('\<lin'+'k\ rel=\"stylesheet\"\ type=\"text\/css\"\
    href=\"..\/Css\/'+sStyleFile+'\"\ media=\"'+sStyleMedia+'\"\ \/\>');}


    if (document.getElementById){

    var OP, Gecko;
    OP =(navigator.userAgent.indexOf('Opera')!=-1);
    Gecko = (navigator.userAgent.indexOf('Gecko')!=-1);

    if (OP)
    {...}

    else if (Gecko)
    {...}

    else
    {...}
    }

    else if (document.all)
    {...}

    else {}

    //-->
    </script>

    Selainkohtaisia huomautuksia:

    1. Jos on tarpeen tunnistaa MS IE -selaimet tarkemmin kuin edellä olleissa esimerkeissä, se voidaan tehdä esim. käyttämällä (navigator.userAgent.indexOf('MSIE 6')!=-1). Sen sijaan on huono idea tehdä versionumeroon perustuvia tunnistusyrityksiä, esim. navigator.appVersion.indexOf("5.")>=0. Versionumero voi tarkoittaa selaimen oman versionumeron lisäksi vastaavan Mozillan versionumeroa, Windows-käyttöjärjestelmän versionumeroa (esim. Windows NT 5.0) tai selaimen jakelukanavan versionumeroa (esim. AOL 5.0). Mahdollinen tunnistusjono voi olla esim. Mozilla/4.0 (compatible; MSIE 6.0; AOL 7.0; Windows NT 5.0).

    2. Jotkut MS IE -selaimen versiot käyttävät poikkeuksellisia versionumeroiden tunnistusjonoja, esim. MSIE+5.5+. Jos versionumeroa kysytään poikkeukselliset versionumerot saattavat jäädä huomioimatta.

    3. Yksilöinnistä puuttuu MS IE Windows ja Mac -selaimien erittely. Ne voi toteuttaa (navigator.userAgent.indexOf('Mac')!=-1) ja (navigator.userAgent.indexOf('Win')!=-1) ehdot lisäämällä. älä käytä käyttöjärjestelmien täydellisiä nimiä, sillä jotkut selaimet käyttävät esim. tunnistusjonoja Win32 ja Mac_PPC.

    4. Operalla on monta esittäytymismoodia ja ne useimmiten osittain "valehtelevat". Opera on yleensä esittäytymismuodossa Esittäydy: MSIE 5.0 (engl. Identify as MSIE 5.0). Opera saattaa saada huonon CSS:n tai se ei saa CSS:ää ollenkaan ratkaisuissa, jossa kysytään vain selainten nimiä ja versionumeroita riippuen tietenkin siitä, millä tavalla selaimet on tunnistettu. Tavat, joilla olen tunnistuttanut Netscape 4.x ja Netscape 6.x+ -selaimet ovat sellaisia, että ne eivät koske Operaa, vaikka selain teeskentelisi olevansa jokin Netscape-selain. Mikäli tunnistukset ovat sisäkkäisiä ne saattavat hidastaa skriptien käsittelyä.

    5. Operan todellista versionumeroa ei ole mahdollista tunnistaa navigator.appVersion.indexOf('5.')>=0 avulla koska Opera vain palauttaa sen selaimen versionumeron, joka se teeskentelee olevansa.

    6. Vaikka Opera olisi missä tahansa esittäytymismoodissa, ainakin sangen uudet versiot voi kuitenkin aina tunnistuttaa omalla nimellään, mutta tämä tunnistus tulee laittaa ennen muita selainkohtaisia tunnistuksia.

    7. Opera 4.x on herkkä sille, että kenoviivan (\) välilyönti merkitään kenoviivalla ja sen jälkeen on vain yksi välilyönti eikä ylimääräisiä rivikatkoja ole. Pienen virheen vuoksi Opera 4.x ei toteuttanut ensimmäisissä testeissäni JavaScript koodausta. Jos rivikatkot tekevät koodin selvemmäksi, koodi täytyy katenoida esim. seuraavasti:

      document.writeln('\<lin'+
         'k\ rel=\"stylesheet\"\ type=\"text\/css\"\'+
         'href=\"..\/Css\/'+sStyleFile+'\"\ media=\"'+
         sStyleMedia+'\"\ \/\>');

    8. Erään saamani s-postin mukaan Opera 3.x -selaimille ei voida varmuudella antaa CSS:ää, joka olisi tarkoitettu selaimille, jotka eivät tue selainkohtaista DHTML:ää tai DOM1:tä sillä selain ei aina ilmoittaisi selaimen omaa versionumeroa. En tiedä onko väite tosi. Ainakin Opera 3.62 EPOC ilmoittaa aina selaimen oman versionumeron.

    9. Kännyköiden kohdalla Symbian OS -selaimet käyttävät esittäytymisjonoissaan käyttöjärjestelmästä jonoa EPOC. Sikäli kuin tiedän vain Opera 5.x+ ovat selaimia, jotka tukevat dynaamisia valikoita.


      Opera for Symbian: General Opera information.
    10. Opera 4.x-5.x -selaimet kärsivät hieman toisessa, kolmannessa ja neljännessä vaihtoehdossa. Ne eivät lue JavaScript koodausta kun käytetään selaimen eteen- ja taaksepäin nuolia (tai muita vastaavia toimintoja). Mikäli vierailija ei valitse joka kerta uutta sivua em. selaimet näyttävät toisessa ja kolmannessa ratkaisussa linkit siten kun muut modernit selaimet näyttävät ne tilanteissa, joissa JavaScript on kytketty pois toiminnasta. Haitta koskee kuitenkin harvoja vierailijoita.

    11. Jos Mozilla Gecko -selaimet halutaan eritellä tarkemmin, suosittelen Build ID eli luontipäivään perustuvia lisätunnistuksia. Ne voidaan liittää esimerkeissäni käyttämiin tunnistuksiin. Esim. (Gecko && (parseInt(navigator.productSub)>=20011018)) koskee Netscape 6.1/ Mozilla 0.9.1 ja sitä uudempia selaimia. Jos haluat voit erottaa Netscape -selaimet Mozilla -selaimista esim. lisäehdolla navigator.vendor == ("Netscape7") ja antaa siten Netscape -selaimille eri päivämäärän kuin Mozilla -selaimille. Periaatteessa erottelun voisi toteuttaa myös tarkennetulla versionumerolla, esim. userAgent.indexOf('Netscape6\/6.1'). Netscapea käsittelevällä sivulla[S] on päivämäärälista ja tunnistusesimerkkejä.

    12. Voit käyttää mallina käyttämääni kommentoitua lähdekoodikatkelmaa[S]. Myös s-postiystäväni sivuilta löytyy lisää tunnistusmalleja.


    13. Linux käyttöjärjestelmässä voidaan käyttää Konqueror-selainta. Selain kirjoittaa oletusarvoisesti selaimen nimen ja versionumeron ja se käyttää omaa esityskonetta. Tällöin se voidaan tunnistuttaa omalla nimellään, mutta mukaan on syytä laittaa lisäehto (käyttämieni esimerkkien pohjalta se olisi && !Gecko), sillä Konqueror-selaimen voi määritellä käyttämään Mozilla Gecko esityskonetta. Selaimen käyttäjä voi kuitenkin muuttaa esittäytymismerkkijonoa, joten selainta ei voi täysin luotettavasti tunnistaa.

    14. Kun suunnitellaan tunnistukset tulevaisuuden selaimien kanssa yhteensopiviksi, voi olla mielekästä eliminoida kirjainten "kastin" vaikutus selainten esittäytymisjonoissa. Laittamalla toLowerCase(). or toUpperCase(). ennen indexOf mahdollinen kirjainkoon vaihtelu ei haittaa (esim. toLowerCase().indexOf('win') toimii sekä jonojen Windows että WINDOWS kanssa).

    15. Käsittelen eräällä toisella sivukokonaisuudella PHP:hen perustuvia selaintunnistuksia[S]

  3. Kaikki linkit ovat itse sivulla, mutta kerrokset määrittelevät elementit tuotetaan skriptien avulla, jolloin kaikki linkit ovat näkyvissä, jos JavaScript on pois päältä. Alla on esimerkki aloitusmerkkauksista (niille pitää kirjoituttaa pariksi päätösmerkkaukset):

    <script language="JavaScript" type="text/javascript">
    <!--
    if (document.layers)
    {document.writeln('\<lay'+'er\ id=\"Kerros1\"\ class="\pageGroup\"\...\>');}

    else {document.writeln('\<di'+'v\ id=\"Kerros1\"\ class="\pageGroup\"\...\>');}

    //-->
    </script>
  4. On myös mahdollista luoda kahden edellisen vaihtoehdon yhdistelmä. Tässä ratkaisussa suurin osa CSS:stä voisi olla päätiedostossa mukaan lukien suurin on kerroksiin liittyvästä CSS:stä. Vain pieni osa olisi erityisesti dynaamisille valikoille tarkoitetussa tiedostossa. Vaikka selain ei lukisi dynaamisille valikoille tarkoitettua CSS-tiedostoa, dynaamiset valikot voisivat toimia ainakin jollakin lailla, jos selain vain luo tarpeelliset elementit.

  5. Valikossa on selkeällä paikalla linkki erityiselle linkkisivulle, josta löytyy jatkolinkit kaikille välttämättömille sivuille. Valikon ei tarvitse olla välttämättä oletusarvoisesti esillä. Valikon paikalla voi olla yksi linkki, joka vie erityiselle linkkisivulle, joka voi olla esim. suuri linkkitaulukko.

  6. Tilanteissa, joissa JavaScript on pois päällä, tulee näkyviin vaihtoehtoinen linkistö. Perus CSS-tiedosto päättyy kohtaan, joka määrittelee dynaamiset linkit piilotettaviksi ja vaihtoehtoiset linkit esitettäviksi. Alla on esimerkki tällaisesta ratkaisusta:

    /* Seuraava CSS on basicScreen.css tiedoston lopussa: */
    #alternativeLinkSet {display:block;} /* Vaihtoehtoisen navigoinnin linkit. */
    #tableAllPages {display:none} /* Dynaamisten valikoiden linkit. */


    /* Seuraava CSS on layers.css-tiedoston alussa: */
    #tableAllPages {display:block}
    #alternativeLinkSet {display:none}

    Selainkohtaisia huomautuksia:

    1. Jos CSS komentaa vaihtamaan ominaisuuksien arvoja paljon CSS:ää on useissa eri tiedostoissa, tilanne kuitenkin "stressaa" selaimia. Huomasin eräässä kokeilussa, jossa selaimen oli tarkoitus saada vaihtoehtoiset linkit tilanteessa, jossa JavaScript-tuki olisi pois päältä, että MS IE 5.0 Windowsilla sivu täytyi ladata uudestaan. Tämä saattoi liittyä CSS:ään (ja/tai servereihin), mutta ei välttämättä, sillä CSS oli osittain JavaScript-riippuvainen. Yksi keino - tosin ei välttämättä toimiva - tämän ongelman välttämiseksi olisi saattanut olla kirjoituttaa CSS skripteillä seuraavan esimerkin tapaisesti myös se, että vaihtoehtoinen linkistö piilotetaan ja dynaaminen linkistö näytetään:

      <script language="JavaScript" type="text/javascript">
      <!--
      {document.writeln('\<sty'+'le\ type=\"text\/css\"\>');
      document.writeln('\<'+'!--');
      document.writeln('#alternativeLinkSet\{display:none\}');
      document.writeln('#tableAllPages\{display:block\}');
      document.writeln('--'+'\>');
      document.writeln('\<\/sty'+'le\>');}

      //-->
      </script>
    2. Edellinen ratkaisu ei poista toista ongelmaa, joka liittyy vaihtoehtoisiin linkkeihin. Vaihtoehtoisen linkistön toinen heikkous on siinä, että JavaScript- tuen poissa ollessa Netscape 4.x näyttää sekä vaihtoehtoiset linkit että dynaamisten valikoiden linkit, sillä JavaScript-tuen pois ottaminen kytkee pois päältä myös CSS-tuen. Myös selaimet, jotka eivät tue lainkaan CSS:ää saavat kaksi linkistöä.

Erilaisissa tavoissa antaa erilainen CSS tilanteille, joissa JavaScript tuki on päällä/ pois päältä on joitakin ongelmia, jotka voivat johtua servereistä, JavaScript-koodin käsittelystä tai CSS:n käsittelystä. Jos palvelin joutuu kasaamaan CSS:n monesta palasta, selain ei välttämättä saa sitä riittävän nopeasti, jolloin se saattaa esittää sivun virheellisesti. Jos yhteydet ovat hyvät, CSS:n ei pitäisi aiheuttaa ongelmia.

[Alku]