Profesní sebevražda

10. 11. 2006 v 13.43

http://sietook.druskova.sk/?ukaz=archiv&id=255#obsah

Otázka pro vás: Proč profesní sebevražda?

Nápověda: Peter Druska je autorem knihy o XHTML a mnoha článků zaobírajících se právě tímto značkovacím jazykem.

Prosím, rozepište se v komentářích.



IE 6+7 bugy: Problémy se seznamy (aktualizováno)

3. 11. 2006 v 13.59

Internet Explorer 6 není dokonalý prohlížeč. Jeho vykreslovací jádro chybuje, zadýchá se pokaždé, když od něj kodér chce byť jen trochu složitější konstrukci podle not CSS 2. Vykreslovací jádro Trident, které je srdcem Internet Exploreru, není však vůbec špatné. Čtvrtá verze tohoto jádra, kterou obsahuje Internet Explorer 6, je dokonce poměrně dobrá na poměry, které vládly ve standardech v letech 2000–2001, kdy bylo CSS2.1 v nedohlednu a CSS2 stále ještě půdou, na niž noha jiného prohlížeče než IE pořádně nevstoupila. To se odvážila až mimořádně povedená Mozilla 1.0 v létě 2002 a použitelná Opera 7 o rok později.

Z předchozího textu si prosím zapamatujte tyto dva údaje:
  • IE6 obsahuje vykreslovací jádro Trident IV,
  • Trident IV byl vyvíjen v letech 2000–2001,

Právě jádro Trident IV přineslo dvě závažné chyby ve vykreslování seznamů, o kterých však prakticky nikdo neví. Jsou dokonce tak neznámé, že si jich nevšimli ani vývojáři vykreslovacího jádra Trident V, které je implementováno v Internet Exploreru 7. Ano, správně, uplynulo celých pět let. Nemějme to však Microsoftu za zlé.

Chyba 1: Nepřičtu se, ani kdyby ses na hlavu stavěl

První chybu a celý tento článek nastartoval Plaváček, když si všiml, že v IE7 se všechny položky číslovaných seznamů označují jako „1.“. Den předtím přidal do inkriminovaného stylopisu tuto deklaraci, aby se vyhnul Zmizíkovi:

* {
  ...
  ...
  min-height: 1px;
}

A právě tato deklarace vypustila dlouho skrývaného Krakena, chybu svými parametry málo vídanou. Pokud nastavíte položce číslovaného seznamu libovolný rozměr (width, height, min-width, min-height), tato položka bude označena jako „1.“. Chyba se projevuje pouze ve vykreslovacích jádrech Trident IV a Trident V (tedy IE6 a IE7). Je zcela irelevantní, zdali se prohlížeč nachází v quirk či standardním módu, pouze je třeba zohlednit fakt, že IE6 v obou módech a IE7 v quirk módu minimální výšku ani šířku neumí. Podívejte se na tento vzorový kód:

<style>
li {
  width: 500px;
}
</style>

<ol>
  <li>test</li>
  <li>test</li>
  <li>test</li>
</ol>

Takovou vlastností je například width. Pokud prvku nastavíte jakoukoliv šírku kromě automatické (width: auto), vlastnost hasLayout se změní na true. To stejné se, pro zajímavost, stane, když nastavíte proprietární vlastnost zoom. Právě nastavení zoom: 1 se dá vhodně využít pro testovací účely. Ale o tom někdy příště.

Jak můžete vidět, v příkladu je pro prvek LI nastavena šířka. Vlastnost hasLayout prvku LI tedy nabývá hodnoty true. V praxi hasLayout nastavené na true mění či ruší účinek některých dalších CSS vlastností, například display. Víte, že prvek seznamu má výchozí vlastnost display nastavenu na hodnotu list-item. Toto nastavení s sebou mimo jiné nese i příznak, který říká, že jádro má u tohoto prvku navyšovat vnitřní počítadlo. Právě tento příznak změna hasLayout ničí.

Je třeba tedy explicitně obnovit stav před změnou hasLayout. Toho dosáhneme snadno deklarací display: list-item. Výsledný kód vypadá následovně:

<style>
li {
  width: 500px;
  display: list-item;
}
</style>

<ol>
  <li>test</li>
  <li>test</li>
  <li>test</li>
</ol>

Chyba 2: Když se odrážky rozhodnou schovat

Pokud je první chyba závažná, pak tato druhá je ještě o chlup závažnější. Projevila se, když jsem si prvně hrál s Plaváčkovým kódem, a její příčina je v podstatě úplně stejná, pouze řešení se liší. Prohlédněte si tento kód:

<style>
ol {
  width: 500px;
}
</style>

<ol>
  <li>test</li>
  <li>test</li>
  <li>test</li>
</ol>

Kód vypadá velmi podobně jako ten předchozí, viďte? Když si ukázku otevřete v Internet Exploreru, uvidíte, že chybějí odrážky. Napadne vás: Aha, ta stejná chyba! Pro seznam je nastavena vlastnost width, takže seznam má hasLayout roven true, to ovlivnilo nějakou vnitřní vlastnost, která zajišťuje vykreslování odrážek.

A máte pravdu. Je to zhruba takto. Ale co s tím? Znám dvě řešení, jedno využívá slabiny quirk módu Internet Exploreru a to druhé funguje. :o) Ukážu vám jen to druhé.

Psst, to první řešení je nastavit display: inline pro seznam, ale je to prasárna!

Když nyní víte, že seznamy vykreslují odrážky mimo hranice kontejneru, jímž jsou určeny, snadno vás už napadne, že změna vlastnosti hasLayout prostě tyto „přebytky“ odřízla. Potřebujete dostat odrážky do kontejneru, na což je naštěstí v CSS vlastnost, a to list-style-position (toto neplatí, čtěte update níže). Stačí ji nastavit na hodnotu inside.

Ukázka řešení:

<style>
ol {
  width: 500px;
  list-style-position: inside;
}
</style>

<ol>
  <li>test</li>
  <li>test</li>
  <li>test</li>
</ol>

Update:

Knopi v komentářích nastínil jiné, v případě seznamů, jejichž položky se mohou zalomit na více řádků, mnohem lepší řešení. Stačí prvkům LI nastavit dostatečný levý margin, aby se čísla dostala zpět do kontejneru. Viz komentáře č. 14 a 16.

Naopak použití vlastnosti list-style-position nedoporučuji. Pokud má seznam více než devět prvků, začne se text položek seznamu „slévat“ dohromady s řadovými číslicemi.

Zdrojový kód nového řešení je v komentáři č. 16.

Závěrem

Určitě se teď ptáte, proč si takových chyb nevšimli vývojáři už dříve. Vysvětlení je snadné. S Plaváčkem jsme se shodli, že v praxi se nesetkáváme se situacemi, kdy bychom potřebovali číslovanému seznamu nastavit šířku či výšku jeho položky. To stejné platí i o druhé chybě – když už potřebujeme zadat seznamu šířku, tak neponecháváme odrážky, protože se téměř vždy jedná o menu, u kterého každý rozumný kodér řeší odrážky, jsou-li třeba, pomocí background-image.

Ani tyto praktické postřehy nic nemění na faktu, že se jedná o chyby závažné, které naštěstí mají jednoduchá řešení. Mějte se hezky.



Image on a background of horizontal rule (HR) without wrapper element in IE

15. 10. 2006 v 9.53

This is a translation of my previous article Obrázek na pozadí horizontální čáry (HR) bez obalového prvku v IE (czech). Vilém Málek convinced me that the best thing I can do is to translate and publish it – so the whole community can profit from it. So don't complain about my horrid English. If you laugh now, stop reading. I really mean it! :o) OK, let's advance to the article.

I'm kind of mythbuster and CSS is my playground. Yesterday while reading some long forgotten bookmarks I noticed that Marek Prokop as well as Petr Václavek didn't succeed when trying to get rid of the annoying border which is surrounding HR element in IE and which prevents this element to be used as a canvas for some kind of fancier separator. I know the solution.

I wanna fancy line and I want it in semantic and practical way

Here is the thing: you have some paragraphs which you wanna have separated using some kind of fancy line or ornament. But you like semantics as well as the users of text browsers, so you want those paragraphs to be separated using <hr> element in the source code. What are the options?

  1. Try styling <hr> element to have needed height and to display desired ornament on its background. Of course, this is the most semantic and practical solution.
  2. Display the ornament using background property on the bottom of the paragraph which will be set larger padding-bottom. Then hide the horizontal rule – <hr> – using display: none.
  3. Inasmuch as you fail realizing option nr. 1, you use <div> element to wrap <hr> and set a background to this wrapper. You hide the horizontal rule using display: none after that.

What's the attitude of typical web-coders?

  • A beginner or a coder, who ignores semantics, uses <div> element anyway and doesn't care about <hr>.
  • An advanced coder, who already created a page or two, will most probably follow the second option.
  • A coder gadgeteer, who takes his work not as a craftsmanship but as an art, will most probably suffer trying to realize option one.
  • An old hand coder, so called pro' or layout-machine, will most probably prefer the first option from the very beginning because he knows, that there few Explorer issues which made him turn grey in the past.

Where is the problem?

You might wonder whats co complicated using the option number one. In that case look at the example.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<title>Image on a background of horizontal rule</title>
<style type="text/css">
hr {
        border: none;
        background: url('image.gif') center center no-repeat;
        height: 15px;
}
</style>

<p>Lorem ipsum...</p>
<hr>

<p>Lorem ipsum...</p>
<hr>

<p>Lorem ipsum...</p>

If you have opened the page in one of the modern browsers, e.g. Firefox, Opera or Safari, you can see the intended result. The paragraphs are separated by three „quarterfoils“, which I borrowed from Marek Prokop. But – if you used Internet Explorer, and it doesn't care which version (yes, including IE7!), you can see some kind of border around the <hr> element even thought I've set border: none in the stylesheet for it.

At this moment are both Marek Prokop and Petr Václavek concordantly shoulder-shrugging. They say the solution doesn't exist.

And hell it does…

… though a wee bit scabrous. But somebody who loves clean and easily usable markup and who leaves all the „dirty work“ to CSS can't complain about it. You try for a while and you find out that setting the color property to match the color of the background destroys the border. Unfortunately – the image on the backgroud as well, this is the wrong way. But – you've learned that what seemed to be border before is actually content (!) of the <hr> element. You're smart, so you have a shot at a negative text-indent trying to scrape the content out of the screen. Voilá, it worked! But „mysteriously“ (explanation is a bit complicated) the content disappeared along with the background image as well. Another wrong way, you're starting to fell like Alice and being pissed you live in the wonderland you delete the whole source code.

That's how I imagine the previous attempts to solve the problem ended. Maybe someone took a look round the Internet and found that there are some unpractical solutions using the clip property (the rule must be absolutely positioned) or filters (the content is reduced to 1×1 px so you can't apply border, background color or texture on the background for the rule). Maybe he used such solution and achieved some kind of Pyrrhic victory, which lacked of elegance and practicalness. So what did I find out?

Using of proprietary zoom property

Maybe you know that Explorer 5.5 has been gifted by brand new zoom property which allows coder to decrease and increase elements on the page. If I tell you that the content border of <hr> has a width of only one pixel and that Internet Explorer is not that bad in rounding, maybe you'll figure it out by yourselves.

All you need is to reduce the line to less than 50% of its width using the zoom property (in fact, the first safe value is 40%) and the width of the border will be rounded down to zero! I'm not going to make it linger – it works. Plus <hr> has one very convenient ability – it keeps its set width whatsoever the zoom is!

What do I need to do to make it work?

  1. Take the original background image, scale it up to create new one that will be four times larger that the original (I strongly recommend working with zoom: 25%) and save it using filename different from original.
  2. Add a conditional comment into the document using which you add new stylesheet that will be visible only to Internet Explorers. Of course the zoom property is not valid, don't put it in your main stylesheet if it's not necessary.
  3. Use the new stylesheet to declare new definitions for horizontal rule. You must change the background-image to point to the new bigger image, then multiply the height property by four as well as border-width, if you are using any.

The solution could look like this one:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<title>Image on a background of horizontal rule</title>
<style type="text/css">
hr {
        border: none;
        background: url('image.gif') center center no-repeat;
        height: 15px;
}
</style>

<!--[if lte IE 7]>
<style type="text/css">
hr {
        background-image: url('four-times-larger-image.gif');
        height: 60px;
        zoom: 25%;
}
</style>
<![endif]-->

<p>Lorem ipsum...</p>
<hr>

<p>Lorem ipsum...</p>
<hr>

<p>Lorem ipsum...</p>

The second link points to a solution showing that coder is able to further style the horizontal rule which is quite useful in many cases.

To sum up…

This is not perfect way to do it, but personally I think it's the first practical way. The one extra image is worth the effect. I don't know about anybody figuring out the solution before me, but if you do or if you know a better way how to achieve such effect, please use the comments to let me know. Maybe I'll show you how to get rid of unwanted „margin“ round the <hr> element next time, thus how to clear safely without the annoying gap in IE.

Comments are welcome!



Obrázek na pozadí horizontální čáry (HR) bez obalového prvku v IE

14. 10. 2006 v 16.08

Rád bořím mýty a hraju si s kaskádovými styly. Dnes jsem si při čtení dávno zaprášených záložek přečetl u Marka Prokopa a Petra Václavka, že se jim v IE nepodařilo zbavit se otravného borderu kolem prvku <hr> tak, aby mohli na pozadí umístit obrázek nějakého jiného, elegantnějšího oddělovače. Já řešení znám.

Chci hezkou čáru, chci ji sémanticky a prakticky

  1. Pokusit se nastylovat prvek <hr> tak, aby měl patřičnou výšku a na pozadí zobrazoval žádaný ornament. Toto je samozřejmě to nejsémantičtější i nejpraktičtější řešení.
  2. Ornament zobrazit pomocí background-image na spodek odstavce, kterému nastavíte větší padding-bottom. Vodorovnou čáru, tedy prvek <hr>, skryjete pomocí display: none.
  3. Vzhledem k tomu, že se vám nepovede realizovat bod jedna, obalíte <hr> prvkem <div>, kterému nastavíte ornament na pozadí, a prvek <hr> skryjete pomocí display: none.

Jak na to půjdou jednotliví kodéři?

  • Začínající kodér či kodér ignorující sémantiku dokumentu použije prvek <div> a na nějaké <hr> se vykašle.
  • Pokročilejší kodér, který už jednu či dvě stránky vytvořil, se pravděpodobně pokusí o bod 2.
  • Kodér-hračička, který chápe kodéřinu jako tvůrčí činnost a ne jako pouhé řemeslo, se bude trápit a pokoušet se o bod 1.
  • Kodér-mazák, profík, stroj na layout, se pravděpodobně uchýlí rovnou k řešení 3, protože ví, že trápení se s bodem 1 už mu kdysi v minulosti přidalo pár vrásek.

Kde je problém?

Můžete se ptát, co je tak obtížného na splnění bodu jedna. V tom případě se podívejte na ukázku.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<title>Obrázek na pozadí prvku HR</title>
<style type="text/css">
hr {
        border: none;
        background: url('obrazek.gif') center center no-repeat;
        height: 15px;
}
</style>

<p>Lorem ipsum...</p>
<hr>

<p>Lorem ipsum...</p>
<hr>

<p>Lorem ipsum...</p>

Pokud jste ukázku otevřeli v nějakém moderním prohlížeči, například Firefoxu, Opeře či Safari, vidíte zamýšlený výsledek. Odstavce jsou odděleny třemi čtyřlísky, které jsem si vypůjčil od Marka Prokopa. Pokud jste však použili libovolnou verzi Internet Exploreru (ano, včetně IE7!), vidíte kolem ornamentu okraje čáry i přes to, že pro prvek <hr> jsem ve stylopisu nastavil border: none;

V tomto okamžiku svorně krčí rameny jak Marek Prokop, tak Petr Václavek. Prý řešení neexistuje.

A just existuje…

… byť maličko krkolomné. Ne však pro někoho, kdo si potrpí na čisťounkém a snadno použitelném markupu a kdo nechává veškerou „špinavou“ práci na kaskádových stylech. Po chvilce zkoušení zjistíte, že když nastavíte vlastnost color tak, aby se rovnala barvě pozadí, rámeček zmizne. S ním však i obrázek na pozadí – tudy tedy cesta nevede, ale zato víte, že to, co se zdálo být rámečkem, je ve skutečnosti obsahem (!) prvku <hr>. Napadne vás tedy prubnout záporný text-indent a tento obsah vysunout pryč z obrazovky. To se povede, bohužel obsah s sebou „záhadně“ (vysvětlení je složitější) vezme i obrázek na pozadí. Další slepá ulička, připadáte si jako Jára Cimrman a naštvaně mažete celý zdroják.

Takhle nějak si představuji, že skončily veškeré předchozí pokusy. Možná se ještě někdo porozhlédl po internetu a zjistil, že existují nepraktická řešení využívající CSS vlastnosti clip (čára musí být absolutně pozicovaná) nebo filtrů (obsah redukován na 1×1 pixel, nelze tedy na čáru například aplikovat border, background-color nebo opakovat vzorek na pozadí). Možná tato řešení použil a dosáhl tak nejvýš pyrrhova vítězství, které však postrádalo eleganci či praktičnost. Co jsem tedy vymyslel já?

Využití nestandardní vlastnosti zoom

Stačí pomocí vlastnosti zoom zmenšit čáru na méně než polovinu (v praxi je nejvyšší funkční hodnota 40% původní velikosti) a šířka rámečku kolem obsahu se zaokrouhlí na nulu. Nebudu vás napínat – funguje to. <hr> má navíc jednu příhodnou vlastnost – ať už ji zoomnete jakkoliv, vždy zachovává nastavenou šířku!

Co je pro to v praxi třeba udělat?

  1. Vzít původní obrázek na pozadí, zvětšit jej na čtyřnásobek (doporučuji pracovat se zoom: 25%) a uložit ho pod jiným názvem.
  2. Přidat do dokumentu podmíněný komentář, kterým přilinkujete speciální stylopis pouze pro Internet Explorery. Vlastnost zoom je samozřejmě nevalidní, nehyzděte jí svůj hlavní stylopis.
  3. V tomto speciálním stylopisu nadefinovat nový styl pro čáru, který se od toho původního bude lišit už zmíněnou vlastností zoom, čtyřnásobě velkým obrázkem na pozadí, dále nastavenou čtyřnásobnou výškou (height) a pokud používáte nějaké ohraničení čáry, tak i jeho čtyřnásobnou šířkou.

Výsledek by mohl vypadat nějak takto:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<title>Obrázek na pozadí prvku HR</title>
<style type="text/css">
hr {
        border: none;
        background: url('obrazek.gif') center center no-repeat;
        height: 15px;
}
</style>

<!--[if lte IE 7]>
<style type="text/css">
hr {
        background-image: url('ctyrnasobne-velky-obrazek.gif');
        height: 60px;
        zoom: 25%;
}
</style>
<![endif]-->

<p>Lorem ipsum...</p>
<hr>

<p>Lorem ipsum...</p>
<hr>

<p>Lorem ipsum...</p>

Druhý odkaz ukazuje, že moje řešení dává kodérovi možnost s takto nastylovanou čárou dále pracovat a upravovat ji.

Závěrem…

Není to úplně ideální způsob, ale osobně jej pokládám za první opravdu použitelný – ten jeden obrázek navíc mně osobně za ten efekt stojí. Nejsem si vědom toho, že by na něco podobného přišel někdo přede mnou, ale pokud tomu tak opravdu je – nebo pokud znáte lepší a čistější řešení – prosím, odkažte na ně v komentářích. Příště vám možná ukážu, jak se v IE zbavit toho otravného marginu-nemarginu nad a pod čárou a jak tedy clearovat bez zbytečného vertikálního rozestupu.

Komentáře k tématu vítány.



document.all a Opera 9

10. 10. 2006 v 10.20

Při tvorbě složitějších javascriptových aplikací občas potřebujete vrátit všechny elementy na stránce. V jedné své aplikaci jsem (v polospánku, rauši či jiném podobném hnutí mysli) použil tuto nebezpečnou konstrukci:

prvky = document.all ? document.all : document.getElementsByTagName('*');

Co hůř, připadlo mi to jako schůdné řešení, instinkty mlčely. Praxe však ukázala své drápky o den později, když jsem aplikace testoval v různých prohlížečích. Deset jich prošlo, ten jedenáctý – Opera 9 – nikoliv. Zkušení javascripteři už si poklepávají na čelo a pro Ty méně zkušené přicházím s vysvětlením.

Ve výše uvedeném kódu testuji prohlížeč na podporu kolekce document.all, kterou představil Internet Explorer ve své čtvrté verzi a kterou také převzali mnohé konkurenční prohlížeče. Tato kolekce má obsahovat všechny prvky na stránce, ale její chování není nikterak závazně popsáno, tzn. ve specifikaci DOM ji nenajdete.

Přesto tuto kolekci podporují všechny tři nejrozšířenější prohlížeče – Explorer, Mozilla/Firefox i Opera. Zatímco podpora u Mozilly je tichá, dotaz if (document.all) vrací hodnotu false, Opera se k podpoře kolekce document.all hrdě hlásí. To znamená, že v Opeře se do proměnné prvky v příkladu nahoře uloží prvky obsažené právě v kolekci document.all. Zde je kámen úrazu.

Opera 9 je důsledná a v této kolekci obsahuje nejen všechny elementy (HTML, HEAD, META, SCRIPT, BODY, P atd.), ale i anonymní textové uzly. Pokud se vyznáte v DOM, víte, o čem mluvím. Pro ostatní drobné zjednodušení – tyto textové uzly se vytvoří všude tam, kde daný element obsahuje libovolná textová data. V elementu <script> jde například o zdrojový kód skriptu, v elementu <p> o text odstavce atd. Takové uzly jsou jiného typu než uzly elementů a nelze s nimi pracovat stejným způsobem, například nenabývají žádné hodnoty metody tagName, která běžně vrací jméno elementu.

Vy však chcete vrátit pouze elementy, textové uzly vás nejspíše nezajímají, proto je vám podobná kolekce k ničemu. Chcete Opeře podstrčit druhý zápis, ten s hvězdičkou a standardní metodou getElementsByTagName. Tady jsem chyboval, když jsem tuto metodu nepoužil jako výchozí. Myslel jsem až příliš na Explorer do verze 5.5, který zápis s hvězdičkou (která říká, že chci vybrat všechny elementy, jedno jakého jména) nedokázal zpracovat. Přitom se nabízí mnohem bezpečnější zápis, a to:

prvky = document.getElementsByTagName('*').length > 0 ? document.getElementsByTagName('*') : document.all;

A to je celé kouzlo.

(Pro pokročilé javascriptaře a pro úplnost dodávám, že při práci s XML dokumenty či fragmenty dokumentů se tato metoda může krutě vymstít a skončit kritickou chybou v prohlížečích, které volání document.all vůbec neznají, v případě, že budeme pracovat s prázdným dokumentem či jeho zlomkem. V takovém případě je jedinou cestou pečlivé testování podpory jednotlivých metod a navrácených výsledků.)