Relativní výška prvku: javascriptový experiment

Pachollini, 4. července 2004, 10:38

Dnes ráno jsem zažil osvícení, dostal geniální nápad, jak se navždy zapsat do análů historie webdesignu zlatým tučným písmem. Vyřeším kvadraturu kruhu, napíšu JavaScript, který umožní nastavovat procentuálně výšku prvků a jejich vertikální umístění vzhledem k rodičovskému prvku. Tím bude jednou provždy odzvoněno tabulkovému designu stránek, debatám o tom, jak nejlépe udělat třísloupcový layout a složitým řešením umístění políčka pro login přesně doprostřed okna uživatelova prohlížeče! Napíšu to samozřejmě podle všech standardů, takže to bude fungovat na věky věků, amen, a s libůstkami Mrkvosoftího takybrowseru jsem zvyklý zápasit dnes a denně, takže si jistě hravě poradím. Když bude mít někdo vypnutý JavaScript, nebude to prostě vypadat tak úžasně sexy, jak si autor stránky představoval, ale což, přístupnost, použitelnost, sémantika, validita a všechny podobné zásadní věci nijak neutrpí.

Hned jsem se do toho pustil, vždyť je to přece tak jednoduché. Příslušné prvky se nějak označí, třeba třídou, po načtení stránky se projede dokument, najdou se označené prvky, spočítá a nastaví se jejich velikost a umístění. (Není to problém, protože ve všech rozumných browserech lze zjistit aktuální velikost prvku.) Pak se nastaví ovladač události tak, aby se to samé provedlo i při změně velikosti okna a je to.

První (a nakonec vlastně i poslední) vážný problém se vyloupl hned poté, co se mi podařilo vypořádat se s tím, že každý browser reportuje velikost prvků na stránce po svém. Pokud se totiž nastaví výška prvku, dojde samozřejmě občas také ke změně velikosti rodiče a design se rozhodí. Veškeré pokusy o nápravu končily beznadějně v nekonečné smyčce. Po pár hodinách experimentování se mi ale nakonec podařilo dosáhnout stavu, kdy k nekonečné smyčce při šikovném požití této vychytávky nedochází. Podmínkou obvykle je, aby v rodičovském prvku byl alespoň jeden potomek, který nemá nastavenou relativní výšku pomocí tohoto úžasného skriptu. A u toho zůstalo. Na druhé straně, občas se mi podařilo dosáhnout zajímavých efektů.

Skript má bohužel i několik dalších chyb, např. chybné chování při nastavení některých CSS vlastností v jiných jednotkách než px. Navíc se mi nepodařilo najít rozumný způsob, jak zjistit, že si uživatel změnil velikost písma, takže místo elegantního ošetření nějaké události skript nakonec hlídá celý design každou sekundu.

Žádný zápis do análů se tedy asi konat nebude, ale což, nemusí být každý den posvícení, zkusím to příště. Třeba to nakonec někdo použije nebo se nechá inspirovat. Pro ty, kteří se nedali odradit a stále je zajímá výsledek mého experimentu, přikládám hotový skript a ukázky aplikace, ale varuji, jde pouze o experiment, žádné řešení.

Návod k použití:

  1. K dokumentu připojíme soubor s JavaScriptem.
  2. Prvkům, které se mají vertikálně zarovnat na střed, nastavíme standardním způsobem třídu middle. Zarovnání provádí skript nastavením vlastnosti margin-top.
  3. Prvkům, které mají mít výšku relativní k rodiči, nastavíme třídu heightNNN, kde NNN je celé číslo v rozsahu 0-100. (Ano, vyjadřuje skutečně procentuální výšku vzhledem k rodiči. Tato výška je včetně marginu, paddingu a borderu, aby nemohlo dojít k roztažení rodiče a následné nekonečné smyčce.)
  4. Do události onload elementu <body> umístíme volání $hset=new c_hset();

Alespoň něco...

Kromě tohoto pochybného výsledku vzniklo jako vedlejší produkt mého snažení také několik javascriptových funkcí, o kterých si myslím, že by možná někomu mohly být užitečné:

function get_current_style($element,$property)
  {
  var ee,$i;
  try
    {
    var $cs=document.defaultView.getComputedStyle($element,'');
    $val=$cs.getPropertyValue($property);
    }
  catch(ee)
    {
    $property=$property.split("-");
    for($i=1;$i<$property.length;$i++) 
      $property[$i]=$property[$i].toUpperCase();
    $property=$property.join("");
    $val=$element.currentStyle.getAttribute($property);
    }
  return $val;
  }

Tato funkce vrátí aktuální hodnotu stylu příslušného prvku. Ve skriptu se používá hlavně ke zjištění velikostí, které se nastvují automaticky při formátování stránky. Pokud tedy např. nastavíme v CSS

div#main{
  margin: 1em 4em;
  width: auto;
  height: auto;
  }

můžeme zjistit aktuální velikost v pixelech pomocí get_current_style(document.getElementById("main","width"); Tato funkce vrátí hodnotu i s jednotkou px, což není zrovna praktické, ale není problém zjistit číselnou hodnotu pomocí jednoduché funkce:

function style2px(hodnota)
  {
  if (hodnota) 
    return Number(hodnota.substr(0,hodnota.indexOf("px")));
  else return 0
  }

Na zjištění aktuální výšky prvku se musí ještě trošku jinak:

function get_current_height($element)
  {
  var ee;
  try
    {
    var $cs=document.defaultView.getComputedStyle($element,'');
    $val=style2px($cs.getPropertyValue("height"));
    }
  catch(ee)
    {
    $val=($element.offsetHeight);
    if($val<0)$val=0;
    }
  return $val;
  }

Pokud potřebejeme najít všechny potomky daného uzlu, které mají přiřazenou určitou třídu, bude se hodit funkce find_class. Pokud ji aplikujeme na objekt document.body, dostaneme všechny prvky s danou třídou v celém dokumentu. Pokud hledáme více tříd, můžeme je oddělit znakem | (bez mezer). Funkce vrací pole odkazů na jednotlivé elementy v DOM reprezentaci dokumentu. Příklad volání: barevne_prvky=find_class(document.body,'cervena|zelena'). Parametry $result a $first slouží pouze pro rekurzivní volání funkce.

function find_class($element,$classnames,$result,$first)
  {
  if(!$first)$first=$element;
  if(!$result)$result=new Array();
  if ($element.nodeType==1)
    {
    var $test_exp=new RegExp("(^| )("+$classnames+")( |$)");
    if($test_exp.test($element.className)) 
      $result[$result.length]=$element;
    }
  if ($element.hasChildNodes()) 
    $result=find_class($element.firstChild,$classnames,$result,$first);
  if ($element.nextSibling && $element!=$first) 
    $result=find_class($element.nextSibling,$classnames,$result,$first);
  return $result;
  }

Funkčnost všech prvků jsem zkoušel v MSIE 5.+, Mozille Firefox 0.91 a Opeře 7.5 pod Windows.

Všechny komentáře ohledně (ne)užitečnosti, (ne)použitelnosti atd. tohoto experimentu vítám.

Co vy na to?

zobrazit všechny komentáře

Aktuální Seky

.