site logo

Dynamický sloupcový graf pomocí AJAX a CSS

V článku uvidíte živou ukázku dynamického sloupcového grafu, který čte data z webového serveru pomocí funkcí Ajax, dále zde najdete popis HTML, CSS i JavaScriptového kódu pro čtení a zobrazení dat, a také pro posouvání grafu. Graf zobrazuje délku odezvy pingu, který provádí webový server.

Ukázka pochází z intranetové aplikace liveLAN, která slouží jako databáze zařízení v lokální síti, obsazení IP adres a pro rychlé testování stavu sítě. Pro testy se osvěčil opakovaný ping a grafické zobrazení výsledků.

Ping nemůže nahradit dokonalejší diagnostické nástroje, ale díky své jednoduchosti je nenahraditelný pro rychlou diagnostitku jak stanic v lokální síti, tak dostupnosti připojení LAN do internetu. Běžná PC se sytémem XP SP1 sice mají defaultně zakázáno odpovídat na ping, ale servery, přepínače a tiskárny, o které nám jde především, na ping většinou odpovídají.

adresa [IP, nebo www]

Do žlutého vstupního pole zadáte adresu cílového zařízení na který se bude provádět ping a tlačítkem start spustíte pravidelné pinkání na tuto stanici. Ping iniciuje webový server a do doby odezvy tedy není zahrnuto zpoždění poslední míle Vaší stanice. Adresa nesmí být delší než 24 znaků, pokud je prázdná, server odpoví náhodnou hodnotou. Během provádění pingu nelze změnit adresu, musíte zastavit pinkání tlačítkem stop. V této ukázce nejde pinkat na PC v lokální síti za firewalem.

Sloupcový graf, který se začne kreslit po startu měření se posunuje vlevo, hodnoty zcela vlevo jsou ztraceny. Hodnoty pro graf jsou získány ze serveru pomocí AJAXu, čas odezvy je přepočten na výšku sloupce. Běžné hodnoty odezvy jsou vykresleny zelenou barvou, hodnoty kratší než 10 msec jsou tmavě zelené s jednotnou výškou sloupce. Ztracený paket je označen červenou barvou a start měření sloupcem šedým. Pinkání na české servery je na páteři poměrně rychlé a doba odezvy minimální, proto při zkoušení zadávejte co nejvíce vzdálenou adresu.

Pod grafem je ukazatel pro výběr sloupce jehož hodnota je zobrazena v digitální formě pod grafem. Pozici nastavíte tažením ukazatele. Dále jsou popsány klíčové funkce, kompletní kód je ve zdrojové části stránky.

Sloupcový graf

Sloupce grafu zde nejsou nakresleny pomocí obrázku img  tak jako v jiných ukázkách na tomto serveru, ale pomocí elementu div, kterému nastavujeme vlastnost stylu border-bottom. Pozor na různá jména stejné vlastnosti stylu. V deklaraci stylů je správný název border-bottom, v kódu javaskriptu je pro tutéž vlastnost nutné použít název borderBottom.

Jednotlivé sloupce grafu mají výšku 220px a šířku 2pixely, z celkové výšky sloupce se prakticky využívá jen dolních 200 pixelů. Sloupce jsou položeny těsně vedle sebe bez mezer. Na pozadí grafu je obrázek, vykreslený jako pozicovaný img, nikoli jako background. Proto lze vytisknout graf včetně pozadí, bez ohledu na aktuální nastavení browseru, kde je obvykle zakázáno tisknout pozadí.


CSS:
.cont {width: 2px; height: 220px; top: 0; float: left;
       border-bottom: 0px solid #ccc;}

HTML:
<div class="cont" id="b0"></div>
<div class="cont" id="b1"></div>
...

Každý sloupec grafu je vytvořen jedním elementem div. Výchozí tloušťka okraje border-bottom  elementů je nastavena na nulu. Každý div má unikátní id,  to nám umožní "adresovat" každý prvek grafu java skriptem a dynamicky mu přiřadit vlastnosti border-bottom  podle hodnoty pingu. CSS vlastnost border-bottom  se skládá ze tří dílčích vlastností: border-bottom-width, border-bottom-style a border-bottom-color, které jsou odděleny mezerou.

Podle standardního box modelu je celková výška elementu určena součtem vnitřní výšky, hodnot padding a border. Když nastavujeme vlastnost border-width, musíme dopočítat a nastavit také vnitřní výšku elementu height v souladu se standardním box modelem, tak aby všechny prvky grafu měly stejnou celkovou výšku.

Browser IE6 vykreslí chybně velmi vysoké sloupce, když v horní části není dost volného prostoru. Takové sloupce jsou posunuty dolů. Proto je element div pro kreslení sloupců o 10% vyšší (220px), než je potřeba v případě ostatních browserů.

Přenos dat pomocí AJAXu

Jak již bylo řečeno, ping provádí webový server. Žádost o provedení pingu iniciuje browser pomocí žádosti XMLHttpRequest. Popis funkcí AJAX již byly uvedeny v článku stav zařízení pomocí AJAX. Menší rozdíl proti uvedenému popisu je v doplnění parametru do požadavku. Syntaxe požadavku je stejná jako při odesílání obsahu formuláře metodou get. Za adresu serverového scriptu je přidán znak otazník, jméno parametru, znak rovná se a hodnota parametru.


function ajaxPing() {
  URL = "/at/dyna/getPingData.asp?ip=" + document.getElementById("ip").value;
  delete req;
  if (window.XMLHttpRequest) {        /* --- IE7, FF, Opera, ... --- */
    req = new XMLHttpRequest();
  }
  else if (window.ActiveXObject) {          /* --- IE6 and older --- */
    req = new ActiveXObject("Microsoft.XMLHTTP");
  }
  if(req) {
    req.onreadystatechange = processReq;
    req.open("GET", URL, true);
    req.send("");
  } 
}

Funkce ajaxPing() vytvoří objekt XMLHttpRequest, sestaví URL adresu serverového skriptu vč. parametru (IP adresa cílového zařízení), nastaví funkci pro příjem odpovědi a požadavek odešle. IP adresa cílového zařízení je získána přímo z formuláře.


function processReq() {
  var time, color, stamp;
  try {
    if(req.readyState == 4) {
      if(req.status == 200) {       /* --- data jsou doručena --- */
        res = req.responseText.split("|");
        stamp =  res[2] + " = ";
        if((res[1]>-1) && (res[1]<=400)) {    /* --- ping: OK --- */
          time  = res[1];
          color = "#5c0";
          stamp = stamp + res[1] + "msec";
          if (res[1] < 10) {         /* --- velmi krátký čas --- */
            time  = 10;
            color = "#370";
          } 
        } else {             /* --- ping: zařízení neodpovídá --- */
          time  = 400;
          color = "#f40";
          stamp = stamp + "n/a";
        }
        displayGraph(time, color, stamp);
        if(running == 1) {   /* --- další ping po 2 sekundách --- */
          window.setTimeout("ajaxPing()", 2000);
        } 
      }
    }
  } catch(e) {}
}

Funkce processReq() očekává odpovědi od serveru. Zpracování odpovědi je provedeno až jsou přijatá data kompletní. Data jsou rozložena na dílčí údaje pomocí funkce split() a podle délky odezvy pingu se nastaví barva sloupce, normalizuje se čas odezvy pingu time  a sestaví se popisný řetězec stamp. Dále je volána funkce pro zobrazení grafu a nakonec se nastaví interval dalšího požadavku na provedení pingu.


req.responseText: www.google.jp|37|2008-06-25 13:42:25

Ukázka zobrazuje příklad datové zprávy, kterou odpovídá server na žádost XMLHttpRequest. Formát zprávy může být v XML, nebo v prostém textu. Zde je použit prostý text. V každém případě serverová i klientská část skriptů musí být programovány s ohledem na dohodnutý formát. Ve zprávě jsou tři údaje, oddělené znakem pipe. První údaj je IP adresa na kterou byl proveden ping pro případnou kontrolu, doba odezvy pingu je na druhém místě (milisekundy). Pokud dojde ke ztrátě paketu je tento údaj = -1. Třetí je časový údaj ze serveru kdy byl ping proveden (ve formátu ISO).

Počet a délka paketů pingu je pevně stanovena v serverovém skriptu. Nelze tedy požadovat extrémě dlouhé pakety s mnohonásobným opakováním a zneužít tak serverový skript k útoku na webová zařízení. Serverový script je závislý na prostředí serveru a není v tomto popsán.

Posouvání grafu

Po přijetí a zpracování dat ze serveru je zavolána funkce displayGraph(...) s parametry čas pingu, barva sloupce a časová značka. Funkce posune graf vlevo a do uvolněného místa zcela vpravo zapíše nová data.


function displayGraph(time, color, stamp) {
  /* --- zobrazi a posune graf zleva doprava --- */
  var R, L = document.getElementById("b0")
  var H = parseInt(time/2);
  for(i=1; i<240; i++) {
    /* --- posunuti sloupcu grafu ulozene ve stylu a attributech --- */
    R = document.getElementById("b" + i);
    L.style.height = R.style.height;
    L.style.borderBottom = R.style.borderBottom;
    L.title = R.title;
    L = R;
  }
  /* --- nova data, zapis do sloupce zcela vpravo --- */
  R.style.height = 220 - H + "px";
  R.style.borderBottom = H + "px solid " + color;
  R.title = stamp;
  showDigi();
}

Funkce nejdříve získá referenci na první sloupec zleva (cílový objekt), ten je označen id="b0", dále přepočítá čas pingu na výšku grafu v pixelech. Nyní v cyklu data přesouváme. Referenci na zdrojový objekt uložíme do proměnné R, přesuneme hodnoty stylu border-bottom, height a atribut title, ve kterém je uchován popisný text (časový údaj kdy byl ping proveden a odezva pingu). Po přesunutí jednoho sloupce změníme referenci, zdrojový objekt se stane cílovým. Po dokončení cyklu proměnná R odkazuje na poslední sloupec vpravo do kterého zapíšeme nová data.

Zobrazení digitálních hodnot

Šipka pod grafem určuje, který sloupec je zobrazen v číselné podobě. Šipkou můžeme posouvat i během měření a přečíst si potřebné digitální údaje. Ukazatel přesouváme pomocí drag&drop, princip byl popsán v článku formuláře datum a čas.


function showDigi() {
  /* --- zobrazí digitální data podle pozice šipky --- */
  x = parseInt((parseInt(document.getElementById("arrow").style.left)+8)/2);
  t = document.getElementById("b" + x).alt;
  document.getElementById("descr").innerHTML = t;
}

Nejdříve vypočteme pozici ukazatele x. Protože .style.left  vrací hodnotu včetně jednotek (zde px), oddělíme jednotky pomocí vnitřní funkce parseInt(), vnější parseInt() zajistí celočíselnou hodnotu, kterou použijeme pro adresaci objektu div  ze kterého vyzvedneme popisný text a uložíme jej do elementu <div id="descr">. Funkce pro zobrazení číselných hodnot je volána po posunutí grafu a při tažení ukazatele.

Možné problémy

Tato stránka funguje správně pouze když máte v browseru zapnutý JavaScript. Délka adresy je omezena na 24 znaků, delší adresy jsou ořezány a proto mohou být vyhodnoceny jako neexistující.

Pokud je počáteční pozice ukazatele určena jen pomocí deklarace CSS, nedařilo se číst hodnotu .style.left. Proto musela být přidána funkce initArrow(), která zapíše do stylu správnou pozici.

Naměřené výsledky v grafu můžete vytisknout, získat screenshot, ale nelze uložit HTML kód, tak aby stránku bylo možné obnovit v původní podobě včetně výsledků měření v grafu. Nastavení, které provádí JavaScript se totiž nepromítají do zdrojového HTML, nebo CSS kódu.

Pokud budete chtít využít kód této stránky na Vašem intranetu, mějte na paměti, že budete potřebovat i kód pro AJAX na Vašem intranetovém serveru, v našem příkladě to je serverový AJAX script getPingData.asp. Browser totiž nemůže z bezpečnostních důvodů žádat data pomocí objektu XMLHttpRequest z jiné domény, než ze které je stažena původní stránka.

updated 10.07.2008