Problém s uvozovkami kolem elementů Q a BLOCKQUOTE 2.

9. září 2002

Přiblížím vám, jak donutit IE 4-6 k automatickému psaní uvozovek, ukáži opravu chybného zanořování uvozovek v Netscape 6+/Mozille a také vysvětlím jedno zajímavé řešení celého problému.

Doplnění uvozovek v IE 4-6/Win

Příklad z minulého článku jsem upravil podle níže uvedeného postupu, abyste do něj mohli během popisu nahlížet.

Protože vlastnosti generovaného obsahu CSS v IE 4-6/Win jsou na tom stále docela bledě, budeme se muset uchýlit k řešení pomocí JavaScriptu. Pro případ, že má uživatel tento skriptovací jazyk vypnutý, je třeba zvýraznit citace alespoň pomocí jednoduchých CSS vlastností, v JavaScriptu pak toto zvýraznění zrušíme. Já jsem se rozhodl pro kurzívu u elementu q, element blockquote jsem nijak neodlišil:

q {font-style: italic;}
q {font-style: inherit;}

Možná si teď říkáte, že jsem se zbláznil, vždyť nejprve nastavuji vlastnost font-style na italic, abych jí vzápětí dal hodnotu inherit, která říká, že se hodnota zdědí z rodičovského prvku (a tedy nebude element q nijak ovlivněn předchozí definicí). Ovšem nezbláznil jsem se já, ale spíše IE 4-6/Win, který v tomto případě chybně dědí hodnotu ne z rodičovského prvku, ale z předchozí definice elementu q. Díky tomu všechny ostatní prohlížeče, které dodoržují specifikaci CSS, zobrazí element q tak, jako by tato dvě pravidla v CSS nebyla, a IE 4-6/Win zobrazí element q kurzívou.

Ještě než se pustíme do samotné JavaScriptové funkce zajišťující doplnění uvozovek, deklarujeme několik proměnných:

var quotes_nav = navigator.userAgent, quotes_i, quotes_elm, quotes_elm2, quotes_level = 0, quotes_ancestors = “, quotes_lastLength = 0;

Všechny názvy proměnných a funkcí jsem začínal předponou quotes_, abyste celý skript mohli snadno začlenit do svého webu, například vložením do souboru se skripty, který načítáte do všech stránek (stejně to můžete udělat i v případě CSS).

V dalším kroku přidáme volání funkce, která zajistí doplnění uvozovek. K tomu dojde tehdy, je-li prohlížečem IE 4-6/Win. Navíc se nesmí jednat o Operu vydávající se za tento prohlížeč. Nepodařilo se mi nalézt způsob, jakým by bylo možné v JavaScriptu zkontrolovat, zda prohlížeč umí vlastnosti generovaného obsahu CSS, proto se musíme řídit jménem a verzí prohlížeče. Pokud o nějakém vhodném způsobu víte, budu rád, když se o něj podělíte v diskusi.

if (quotes_nav.indexOf(‚MSIE‘)!=-1 && quotes_nav.indexOf(‚Opera‘)==-1 && parseFloat(quotes_nav.substr(quotes_nav.indexOf(‚MSIE‘)+5,4))<=6 && quotes_nav.indexOf(‚Win‘)!=-1)
{
  document.write(‚<style type=“text/css“>q {font-style: normal;}<‚ + ‚/style>‘);
  var quotes_interval = setInterval(‚quotes_fillIn()‘,10);
}

Uživatel má v prohlížeči zapnutý JavaScript, proto jsme nejprve opravili vlastnost font-style elementu q. Tento element tedy zdůrazníme uvozovkami, ne kurzívou. Podřetězec </ jsme rozdělili na <' + '/. První výskyt znaků </ ve skriptu je totiž považován za uzavírací část elementu script, tedy za konec skriptové sekce.

Doplnění uvozovek zajišťuje funkce quotes_fillIn(), která se bude spouštět v intervalu 10 milisekund až do kompletního načtení stránky do prohlížeče (viz dále). Uvozovky se tak budou doplňovat průběžně během načítání stránky a uživatel nic nepozná. Nyní se na tuto funkci můžeme podívat:

function quotes_fillIn()
{
  for (quotes_i = quotes_lastLength; quotes_i < document.all.length; quotes_i++)
  {
    quotes_elm = document.all[quotes_i];
    if (quotes_elm.tagName.toLowerCase()==’q‘)
    {
    quotes_setLevel(quotes_elm);
    quotes_elm.innerHTML = (quotes_level==0 ? ‚„‘ : quotes_level==1 ? ‚‚‘ : quotes_level==2 ? ‚»‘ : quotes_level==3 ? ‚›‘ : “) + quotes_elm.innerHTML + (quotes_level==0 ? ‚“‘ : quotes_level==1 ? ‚‘‘ : quotes_level==2 ? ‚«‘ : quotes_level==3 ? ‚‹‘ : “);
    quotes_level = 0;
    }
    else if (quotes_elm.tagName.toLowerCase() == ‚p‘ && quotes_elm.parentElement.tagName.toLowerCase() == ‚blockquote‘) quotes_elm.innerHTML = ‚„‘ + quotes_elm.innerHTML + ‚“‘;
  }
  quotes_lastLength = document.all.length;
  if (document.readyState == ‚complete‘) clearInterval(quotes_interval);
}

Vzhledem k tomu, že je tato funkce určena výhradně pro IE 4-6/Win, rozhodl jsem se využít mnoha jeho nestandardních vlastností. IE 5+ již sice obsahuje dostatečnou podporu DOM na to, aby se to dalo napsat standardně, ale i tak bychom museli kvůli IE 4 používat tyto nestandardní vlastnosti a navíc bychom si tím přidělali práci v podobě speciálních větví if určených pro IE 4. Proto byste většinu z nich neměli brát jako příklad správného JavaScriptu, na internetu jsou vhodné jen v případech, kdy píšete pouze pro IE.

Celá funkce pracuje na jednoduchém principu. Pomocí nestandardní kolekce document.all prochází pole všech elementů stránky a kontroluje jméno aktuálního elementu (standardní vlastnost tagName). Pokud je toto jméno q, tak:

  1. Zjistí, jestli je tento element q uvnitř jiného elementu, kolem kterého se dělají uvozovky (další q nebo p, které je dítětem blockquote). To všechno pomocí funkce quotes_setLevel(), které se jako parametr předává odkaz na aktuální element. Tuto funkci si probereme za chvilku, nyní nám stačí vědět, že jejím úkolem je nastavit proměnnou quotes_level na číslo, které odpovídá počtu elementů, kolem kterých se dělají automatické uvozovky a které jsou předchůdci aktuálního elementu (tzn. aktuální element je jejich potomkem, je v nich vnořen).
  2. Podle toho, v kolika elementech, kolem kterých se dělají uvozovky, je aktuální element vnořen, jsou kolem jeho obsahu udělány uvozovky pomocí nestandardní vlastnosti innerHTML. Uvozovky se tak stávají součástí elementu, na což je třeba dávat pozor při pozdější manipulaci s dokumentem pomocí DOM, protože v prohlížečích, doplňujících uvozovky pomocí CSS nebo automaticky, tyto uvozovky nejsou součástí stromu dokumentu. Uvozovky se také, narozdíl od normálu, nacházejí uvnitř elementu, takže jsou na ně aplikovány všechny CSS vlastnosti aplikované na element, zatímco u prohlížečů doplňujících uvozovky pomocí CSS nebo automaticky, jsou na uvozovky aplikovány pouze dědičné CSS vlastnosti elementu, před nebo za který jsou vloženy, ostatní CSS vlastnosti mají implicitní hodnotu.

Pokud je jméno aktuálního elementu p a jméno jeho rodičovského elementu (nestandardní vlastnost parentElement) blockquote, potom se rovnou doplní uvozovky „“. Tento element totiž nemůže být vnořen v žádném jiném elementu, kolem kterého se píší uvozovky: p nesmí obsahovat další p, řádkový (inline) element q nemůže obsahovat ani blokové (block) elementy, jako jsou p nebo blockquote.

Když projde funkce quotes_fillIn() všechny dostupné elementy, uloží jejich počet do proměnné quotes_lastLength (ta je na počátku inicializována na hodnotu 0) a pomocí nestandardní vlastnosti readyState zjistí, jestli už je dokument načtený. Pokud ano, zastaví interval quotes_interval a funkce již dále neprobíhá.

Pokud není ještě dokument načtený, spustí se díky intervalu funkce za 10 milisekund znovu, neopakuje ale kontrolu elementů, které jí už prošly (právě díky proměnné quotes_lastLength).

Nyní nám zbývá funkce quotes_setLevel(), která nastavuje proměnnou quotes_level:

function quotes_setLevel(quotes_elm2)
{
  quotes_ancestors += ‚.parentElement‘;
  if (eval(‚quotes_elm2‘ + quotes_ancestors + ‚.tagName.toLowerCase()‘) == ‚q‘ || (eval(‚quotes_elm2‘ + quotes_ancestors + ‚.tagName.toLowerCase()‘) == ‚p‘ && eval(‚quotes_elm2‘ + quotes_ancestors + ‚.parentElement.tagName.toLowerCase()‘) == ‚blockquote‘)) quotes_level++;
  else if (eval(‚quotes_elm2‘ + quotes_ancestors + ‚.tagName.toLowerCase()‘) == ‚body‘)
  {
    quotes_ancestors = “;
    return true;
  }
  quotes_setLevel(quotes_elm2);
}

Princip této funkce je také jednoduchý. Postupně prochází všechny předky aktuálního elementu (tedy elementy, v kterých je aktuální element vnořen) pomocí nestandardní vlastnosti parentElement, která odkazuje na rodičovský element (tedy element, ve kterém je aktuální element přímo vnořen). Funkce neustále spouští sebe sama, přičemž se pokaždé posune na vyšší element v hierarchii předků aktuálního elementu. Když narazí na element se jménem body, vynuluje pomocnou proměnnou a zastaví se. U každého předka aktuálního elementu zkontroluje, zda jeho jméno není q nebo p, který je dítětem (tedy je přímo vnořen v) blockquote. Pokud ano, přičte jedničku k proměnné quotes_level. Tato proměnná je po skončení funkce quotes_setLevel() použita ve funkci quotes_fillIn() a vzápětí nastavena na hodnotu 0, na kterou byla inicializována i na počátku.

To je tedy celý JavaScriptový kód, který zajišťuje doplnění uvozovek v IE 4-6/Win. Až spatří světlo světa nová verze IE, měli byste zkontrolovat podporu automatických uvozovek v ní, a případně ji přidat do podmínky, která spouští funkce pro doplnění uvozovek, musíte se však ujistit, že stále zná nestandardní vlastnosti, které zde používáme.

Pro správný chod funkce i CSS deklarace je také nutné, abyste dodržovali syntaktická pravidla (X)HTML a navíc do elementu blockquote vnořovali elementy p přímo – tedy abyste je neobalovali žádnými jinými elementy, například div. Specifikace HTML 4.01 také umožňuje úplně vynechat elementy html, head a body. Pokud však této možnosti využíváte, naprosto tím zmatete funkci quotes_setLevel(), a proto se tomu buď musíte vyhnout (což bych doporučoval, protože v XHTML to již povoleno není), nebo si funkci upravit.

Oprava chyby vnořování v Netscape 6+/Mozille

Jak jsme si již řekli, Netscape 6+/Mozilla sice uvozovky doplňuje sám (sama), ale neovládá jejich zanořování. Zanoření se v praxi využije opravdu zřídkakdy (v příkladu jsem úmyslně použil trojité, ale jinak si neumím představit moc smysluplných vět s takovýmto zanořením). Tak jako tak, funkce zanoření se dá jednoduše simulovat pomocí CSS:

blockquote > p:before {content: „„“;}
blockquote > p:after {content: „““;}
q:before {content: „„“;}
q:after {content: „““;}
blockquote > p q:before {content: „‚“;}
q q:before {content: „‚“;}
blockquote > p q:after {content: „‘“;}
q q:after {content: „‘“;}
blockquote > p q q:before {content: „»“;}
q q q:before {content: „»“;}
blockquote > p q q:after {content: „«“;}
q q q:after {content: „«“;}
blockquote > p q q q:before {content: „›“;}
q q q q:before {content: „›“;}
blockquote > p q q q:after {content: „‹“;}
q q q q:after {content: „‹“;}

(třetí příklad)

Určitě vidíte, že některá pravidla by se dala sdružit do jednoho. Neudělal jsem to, protože Opera neovládá sdružené selektory (s ,) u pseudo-tříd :before, :after a celé pravidlo tak ignoruje.

Tato pravidla v podstatě plně suplují pravidla zmiňovaná minule. Ta se tak stávají zbytečnými, a proto jsem je vyřadil. „Nová“ pravidla však mají jednu chybu – pokud se rozhodnete používat automatické uvozovky i u jiných elementů, nebude prohlížeč počítat s jejich zanořováním vzhledem k elementům q a blockquote. Tuto funkci můžete opět suplovat pomocí podobných pravidel, ale asi to není ideální řešení. Ponechal bych původní pravidla, protože k zanořování se dostanete stejně málokdy, navíc není zase taková tragédie, pokud do sebe budou vnořeny dvoje stejné uvozovky. Dá se také předpokládat, že tato chyba bude v Mozille brzy opravena.

Finální řešení – efektní i efektivní

Na závěr bych vám rád představil řešení, které je mým osobním favoritem. Jedná se o to, že kolem odstavců v elementu blockquote použijeme vcelku neobvyklé, lehce ozvláštňující uvozovky »« a kolem elementu q definujeme soubor uvozovek „“‚‘›‹, takže se nebudou žádné z nich opakovat. Zde již opravdu nevidím důvod používat opravu pro Mozillu, protože byste k jejímu využití museli vnořit do sebe dva elementy q.

Samozřejmě musíme odpovídajícím způsobem upravit CSS deklaraci i JavaScriptové funkce (obojí se zjednoduší), ale není to nic složitého, proto zde uvedu jen odkazy na čtvrtý příklad (bez opravy pro Mozillu, podle mě nejlepší řešení) a pátý příklad (s opravou pro Mozillu). Můžete si také stáhnout všechny příklady v ZIP archivu.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *