Click here to Skip to main content
Click here to Skip to main content

AJAX Charts with JavaScript and ASP.NET or PHP

, 30 Apr 2012
Rate this:
Please Sign up or sign in to vote.
An example to show JavaScript charts with ASP.NET or PHP

Introduction 

Looking at actual stock charts it does not seem to be possible to draw Ajax charts without flash, silverlight or any other plug-in. Well, with Ajax in connection with canvas it should be possible. But also without canvas it is possible to show dynamic charts and it works also with Internet Explorer 6.0.

Background 

One problem using Ajax charts is the resulting high server load due to too many SQL requests, but this can be avoided. The easiest way from my view is the use of simple data files. For test purposes I have added source code to simulate a server that writes a data file. For the implementation I have used the free library from www.running-charts.com. The library is rather small and seems to work well with Ajax stock charts.   

The Data File  

The data file for the chart is implemented as a text file. Every line in the text file contains a value and, separated by a semicolon, the corresponding time slot. I put the time slot instead of the time itself into the data file because this reduces the amount of data to be transferred to the client and well - I was too lazy to calculate the time slot at the client side.

Please excuse that I've put the data file at the same place as the html files, but that makes this example a little bit easier to understand and easier to install. It is better to put the data file somewhere else before using the code, because otherwise it would be possible to access it directly from the Internet.

Time slot   

A time slot is the smallest time unit (1 pixel wide) that can be shown in a chart. For example if a chart is 180 pixels wide and shows 3 hours data, then every time slot represents 60 seconds data.  

The PHP Chart Solution 

The PHP chart solution comes with three little files: The server file php.php5. It contains the backend function ReadData used as the data source and the functions to simulate a server process that writes data to the data source file. 

<?php 

  function UpdateTillTimeSlot ($nTimeSlot, $nStartTime)
  {
    clearstatcache();
    $nTimeDiff = time() - $nStartTime;
    if ($nTimeSlot <= $nTimeDiff && $nTimeSlot < 360) 
    {
      $datfile = fopen ("data.txt","a+");
      while ($nTimeSlot <= $nTimeDiff) 
      {
        $val = sin($nTimeSlot/360*6.28)*10000+10000; // Values between 0 and 20000
        $strVal = sprintf ("%d;%d",$val,$nTimeSlot);
        fwrite ($datfile,$strVal."\r\n");
        $nTimeSlot += 1; // Set increment to 10 to see how it works when there is not much data
        if ($nTimeSlot >= 360)
        {
          fwrite ($datfile,"End\r\n"); // Marker for end
          $nTimeSlot = -1;
          break;
        }
      }
      fclose ($datfile);
    }
    return $nTimeSlot;
  }

  function ReadData(&$oldPos)
  {
    $result = "";
    $fileLength = filesize("data.txt");
    $datfile = fopen ("data.txt","r");

    $maxwait = 60;
    while ($fileLength <= $oldPos && $maxwait-- > 0) // Wait for data
    {
      sleep(1);
      clearstatcache();
      $fileLength = filesize("data.txt");
    }

    if ($fileLength>$oldPos)
    {
      fseek ($datfile,$oldPos);
      $data = fread ($datfile,$fileLength-$oldPos);
      $bytesRead = strlen($data);
      while ($bytesRead > 0 && $data[--$bytesRead] != "\n") { } //Work only with complete Items
      $pos = 0;
      while ($pos < $bytesRead)
      {
        $result .= "<i>";
        while ($data[$pos] != "\r")
        {
          $result .= $data[$pos];
          $pos++;
        }
        $result .= "</i>";
        $pos++;
        $pos++;
      }
      if ($data[$bytesRead] == "\n")
      {
        $bytesRead++;
      }
    }
    $oldPos += $bytesRead;
    fclose ($datfile);
    return $result;
  }


  header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
  header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
  header('content-type: text/xml; charset=utf-8;',true);

  $func=$_POST["func"];
  if ($func=="")
  {
    $func=$_GET["func"];
  }
  if ($func && $func=="start")
  {
    $datfile = fopen ("data.txt","w+");
    $startTime = sprintf ("%d",time());
    fclose ($datfile);
    echo "<?xml version='1.0' encoding='UTF-8' ?>";
    echo "<Result StartTime='".$startTime."'></Result>";
  }
  if ($func && $func=="UpdateTillTimeSlot")
  {
    $timeSlot=$_POST["timeSlot"];
    if ($timeSlot=="")
    {
      $timeSlot=$_GET["timeSlot"];
    }
    $startTime=$_POST["startTime"];
    if ($startTime=="")
    {
      $startTime=$_GET["startTime"];
    }
    $timeSlot = UpdateTillTimeSlot ($timeSlot,$startTime);
    echo "<?xml version='1.0' encoding='UTF-8' ?>";
    echo "<Result TimeSlot='".$timeSlot."'></Result>";
  }
  if ($func && $func=="ReadData")
  {
    $oldPos=$_POST["oldPos"];
    if ($oldPos=="")
    {
      $oldPos=$_GET["OldPos"];
    }
    $result = ReadData($oldPos);
    echo "<?xml version='1.0' encoding='UTF-8' ?>";
    echo "<Result FilePointer='".$oldPos."'>".$result."</Result>";
  }
?>  

The second file chart-php.html visualizes the Ajax driven data stream from php.php5. 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
 <meta http-equiv="Content-Script-Type" content="text/javascript"/>
 <meta http-equiv="Content-Style-Type" content="text/css" />
 <title>PHP AJAX Chart Sample</title>
 <style type="text/css">
  .ChartMain { cursor: move }
 </style>
 <script type="text/javascript" src="chartlib11free.js"></script>
 <script type="text/javascript">
 /* <![CDATA[ */

var http_request = false;
var g_objFillUpChart;
var g_lastItem;
var g_pos;

function ifDebug()
{
   var urlArray = document.URL.split("#");
   if (urlArray[1] && urlArray[1]=="D")
   {
      return true;
   }
   else
   {
      return false;
   }
}

function GetXmlAttribute(xmlNode,name)
{
   if(xmlNode.getAttribute(name))
   {
      return xmlNode.getAttribute(name);
   }
   return "";
}

function createAjaxObj()
{
   var httpRequest = false
   if (window.XMLHttpRequest)
   { // if Mozilla, Safari ...
      httpRequest = new XMLHttpRequest()
      if (httpRequest.overrideMimeType)
      {
         httpRequest.overrideMimeType('text/xml')
      }
   }
   else
   {
      if (window.ActiveXObject)
      { // if IE
         try 
         {
            httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
         } 
         catch (e)
         {
            try
            {
               httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch (e)
            {
            }
         }
      }
      else
      {
         alert ('Please allow the use of ActiveX (save for scripting)');
      }
   }
   if (!httpRequest) 
   {
      alert('End: (Could not create XMLHTTP-Instance)');
      return false;
   }
   return httpRequest
}

function makeRequest(url,strData,Asynchron,doresult) 
{
   http_request = createAjaxObj();
   http_request.onreadystatechange = doresult;
   http_request.open('POST', url, Asynchron);
   http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
   http_request.send(strData);
}

function CreateChart(chartDiv,chartType,tXLabel,tYLabel)
{
   g_objFillUpChart = new FillUpChart(tXLabel,tYLabel,410,360,220,200,true,new ColorPara ("black","green","red","blue"),chartType,new ComparePara(10000,"yellow",1));
   g_objFillUpChart.SetXLabels("gray",10,0,2,"8pt Arial","gray");
   g_objFillUpChart.SetXRange(0,359);
   g_objFillUpChart.SetXLabel(0,"start");
   g_objFillUpChart.SetXLabel(180,"middle");

   g_objFillUpChart.SetYLabels(7,"gray",7,10,5,5,"8pt Arial","gray");
   g_objFillUpChart.ActualValue(tXLabel,"blue",5,"blue",8,"8pt Arial");
   g_objFillUpChart.MaxValue(tXLabel,"green",5,"green",8,"8pt Arial");
   g_objFillUpChart.MinValue(tXLabel,"red",5,"red",8,"8pt Arial");

   g_objFillUpChart.ScaleY(0,0,0,20000);

   var divChart = document.getElementById (chartDiv);
   divChart.appendChild(g_objFillUpChart.divNode);
}

function OnUpdateComplete()
{
   if (http_request.readyState == 4) 
   {
      if (http_request.status == 200) 
      {
         var bEnd = false;
         if (http_request.responseXML.documentElement.firstChild)
         {
            var element = http_request.responseXML.documentElement.firstChild;
            while (element)
            {
               if (element.nodeType==1 && element.nodeName=="i")
               {
                  var data;
                  if (element.firstChild.text)
                  {
                     data = element.firstChild.text;
                  }
                  else
                  {
                     data = element.firstChild.textContent;
                  }
                  if (data!="End")
                  {
                     var arrDat = data.split(";");
                     g_objFillUpChart.SetPoint(arrDat[1],arrDat[0]);
                  }
                  else
                  {
                     bEnd = true;
                  }
               }
               element=element.nextSibling;
            }
         }
         if (bEnd==false)
         {
            Update(http_request.responseXML.documentElement.getAttribute("FilePointer"));
         }
      }
      else 
      {
         alert("A problem while requesting: "+http_request.status);
      }
   }
}

function Update(filePointer)
{
   var strData = "func=ReadData";
   strData+="&oldPos="+filePointer;
   if (ifDebug())
   {
      alert (strData);
   }
   makeRequest("php.php5",strData,true,OnUpdateComplete);
}

 /* ]]> */
 </script>
</head>
 <body onload="CreateChart('chart-window-01',ChartType.HighLowActualDiff,-1,-1);Update();">
  <div style="position:absolute; top:120px; left:360px; width:600px; font-size:13pt;">
   <a id="running-charts.com" style="position:relative; left:27px; font-size:10pt;" href="http://www.running-charts.com/">running-charts.com</a>
  </div>
  <div id="chart-window-01" style="position:absolute; top:140px; left:360px; width:600px; font-size:13pt;">
  </div>
  <br/>
 </body>
</html>

The file updater.html simulates a server process that writes data to the data source file. 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
 <meta http-equiv="Content-Script-Type" content="text/javascript"/>
 <meta http-equiv="Content-Style-Type" content="text/css" />
 <title>PHP Chart-Server Simulation</title>
 <script type="text/javascript">
 /* <![CDATA[ */

var http_request = false;
var g_startTime;

function ifDebug()
{
   var urlArray = document.URL.split("#");
   if (urlArray[1] && urlArray[1]=="D")
   {
      return true;
   }
   else
   {
      return false;
   }
}

function GetXmlAttribute(xmlNode,name)
{
   if(xmlNode.getAttribute(name))
   {
      return xmlNode.getAttribute(name);
   }
   return "";
}

function createAjaxObj()
{
   var httpRequest = false
   if (window.XMLHttpRequest)
   { // if Mozilla, Safari ...
      httpRequest = new XMLHttpRequest()
      if (httpRequest.overrideMimeType)
      {
         httpRequest.overrideMimeType('text/xml')
      }
   }
   else
   {
      if (window.ActiveXObject)
      { // if IE
         try 
         {
            httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
         } 
         catch (e)
         {
            try
            {
               httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch (e)
            {
            }
         }
      }
      else
      {
         alert ('Please allow the use of ActiveX (save for scripting)');
      }
   }
   if (!httpRequest) 
   {
      alert('End: (Could not create XMLHTTP-Instance)');
      return false;
   }
   return httpRequest
}

function makeRequest(url,strData,Asynchron,doresult) 
{
   http_request = createAjaxObj();
   http_request.onreadystatechange = doresult;
   http_request.open('POST', url, Asynchron);
   http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
   http_request.send(strData);
}

function OnUpdateDataComplete() 
{
   if (http_request.readyState == 4) 
   {
      if (http_request.status == 200) 
      {
         if (ifDebug())
         {
            alert(http_request.responseText);
         }
         var timeSlot = GetXmlAttribute(http_request.responseXML.documentElement,'TimeSlot');
         var state = document.getElementById ("state");
         state.innerHTML= timeSlot;
         if (-1 != timeSlot)
         {
             setTimeout(function (){UpdateData(timeSlot,g_startTime);},1000);
         }
      }
      else 
      {
         alert("A problem while requesting: "+http_request.status);
      }
   }
}

function UpdateData (timeSlot,startTime)
{
   var strPara = "func=UpdateTillTimeSlot";
   strPara+="&timeSlot="+timeSlot;
   strPara+="&startTime="+startTime;
   if (ifDebug())
   {
      alert (strPara);
   }
   makeRequest("php.php5",strPara,true,OnUpdateDataComplete);
}

function OnStartComplete() 
{
   if (http_request.readyState == 4) 
   {
      if (http_request.status == 200) 
      {
         if (ifDebug())
         {
            alert(http_request.responseText);
         }
         g_startTime = GetXmlAttribute(http_request.responseXML.documentElement,'StartTime');
         if (ifDebug())
         {
            alert(g_startTime);
         }
         var state = document.getElementById ("state");
         state.innerHTML= 0;
         setTimeout(function (){UpdateData(0,g_startTime);},1000);
      }
      else 
      {
         alert("A problem while requesting: "+http_request.status);
      }
   }
}

function start()
{
   var strData = "func=start";
   if (ifDebug())
   {
      alert (strData);
   }
   makeRequest("php.php5",strData,true,OnStartComplete);
}

 /* ]]> */
 </script>
</head>
 <body onload="start();">
  <h1>PHP Chart Data Updater</h1>
  <span>Timeslot: </span><span id="state"></span>
 </body>
</html>

The source for the chart values is a sinus function. So after a successful installation of these 3 files to a PHP 5 enabled server, the page with chart-php.html should show a growing sinus chart if the page updater.html is also started before.

The ASP.NET Chart Solution  

The ASP.NET chart solution comes with only two files. The file chart.aspx contains the server side with the read of the data source and the client side with the visualization of the data in the same file.

<%@ Page Language="C#" %>
<%@ Register Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.UI" TagPrefix="asp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>ASP.NET AJAX Chart Sample</title>
    <script runat="server">
        [System.Web.Services.WebMethod(BufferResponse = false)]
        [System.Web.Script.Services.ScriptMethod(ResponseFormat = System.Web.Script.Services.ResponseFormat.Xml)]
        public static String ReadData(int nOldPos)
        {
            int nBytesRead = 0;
            String result = "";
            using (System.IO.FileStream fileStream = new System.IO.FileStream(HttpContext.Current.Request.MapPath("~/data.txt"), System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite, 1024, false))
            {
                int nMaxwait = 60;
                while (fileStream.Length <= nOldPos && nMaxwait-- > 0) // Wait for data
                {
                    System.Threading.Thread.Sleep(1000);
                }
                if (fileStream.Length > nOldPos)
                {
                    fileStream.Seek(nOldPos, System.IO.SeekOrigin.Begin);
                    int nBytes = (int)fileStream.Length - nOldPos;
                    byte[] data = new byte[nBytes];
                    nBytesRead = fileStream.Read(data, 0, nBytes);
                    while (nBytesRead > 0 && data[--nBytesRead] != '\n') { } // Work only with complete Items
                    int nPos = 0;
                    while (nPos < nBytesRead)
                    {
                        result += "<i>";
                        while (data[nPos] != '\r')
                        {
                            result += (char)data[nPos];
                            nPos++;
                        }
                        result += "</i>";
                        nPos++;
                        nPos++;
                    }
                    if (data[nBytesRead] == '\n')
                    {
                        nBytesRead++;
                    }
                }
            }
            return "<Result FilePointer='" + (nOldPos + nBytesRead).ToString() + "'>" + result + "</Result>";
        }
    </script>
    <script type="text/javascript" src="chartlib11free.js"></script>
    <script type="text/javascript">
        var g_objFillUpChart;
        function Update(filePointer)
        {
            PageMethods.ReadData(filePointer, OnUpdateComplete);
        }
        function OnUpdateComplete(result)
        {
            var bEnd = false;
            if (result.documentElement.firstChild)
            {
                var element = result.documentElement.firstChild;
                while (element)
                {
                    if (element.nodeType==1 && element.nodeName=="i")
                    {
                        var data;
                        if (element.firstChild.text)
                        {
                            data = element.firstChild.text;
                        }
                        else
                        {
                            data = element.firstChild.textContent;
                        }
                        if (data!="End")
                        {
                            var arrDat = data.split(";");
                            g_objFillUpChart.SetPoint(arrDat[1],arrDat[0]);
                        }
                        else
                        {
                            bEnd = true;
                        }
                    }
                    element=element.nextSibling;
                }
            }
            if (bEnd==false)
            {
                Update(result.documentElement.getAttribute("FilePointer"));
            }
        }
        function CreateChart(chartDiv,chartType,tXLabel,tYLabel)
        {
            g_objFillUpChart = new FillUpChart(tXLabel,tYLabel,410,360,220,200,true,new ColorPara ("black","green","red","blue"),chartType,new ComparePara(10000,"yellow",1));
            g_objFillUpChart.SetXLabels("gray",10,0,2,"8pt Arial","gray");
            g_objFillUpChart.SetXRange(0,359);
            g_objFillUpChart.SetXLabel(0,"start");
            g_objFillUpChart.SetXLabel(179,"middle");
            g_objFillUpChart.SetYLabels(7,"gray",7,10,5,5,"8pt Arial","gray");
            g_objFillUpChart.ActualValue(tXLabel,"blue",5,"blue",8,"8pt Arial");
            g_objFillUpChart.MaxValue(tXLabel,"green",5,"green",8,"8pt Arial");
            g_objFillUpChart.MinValue(tXLabel,"red",5,"red",8,"8pt Arial");
            g_objFillUpChart.ScaleY(0,0,0,20000);
            var divChart = document.getElementById (chartDiv);
            divChart.appendChild(g_objFillUpChart.divNode);
        }
    </script>
</head>
<body onload="CreateChart('chart-window-01',ChartType.HighLowActualDiff,-1,-1);Update(0);">
    <h1>ASP.NET AJAX Chart Sample</h1>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" />
    </form>
    <div style="position:absolute; top:120px; left:270px; width:600px; font-size:13pt;">
        <a id="running-charts.com" style="position:relative; left:27px; font-size:10pt;" href="http://www.running-charts.com/">running-charts.com</a>
    </div>
    <div id="chart-window-01" style="position:absolute; top:140px; left:270px; width:600px; font-size:13pt;">
  </div>
</body>
</html>

The file updater.aspx contains the server side simulation of a server that writes data to the data source and the corresponding client side surface in the same file.

<%@ Page Language="C#" %>
<%@ Register Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.UI" TagPrefix="asp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ASP.NET AJAX Web Services: Web Service Sample Page</title>
    <script runat="server">
        [System.Web.Services.WebMethod(BufferResponse = false)]
        [System.Web.Script.Services.ScriptMethod()]
        public static long UpdateTillTimeSlot(long nTimeSlot, long nStartTime)
        {
            System.DateTime dateTime = System.DateTime.Now;
            System.DateTime date1970 = new System.DateTime(1970, 1, 1, 0, 0, 0);
            System.TimeSpan timeDiff = dateTime.Subtract(date1970);
            long nTimeDiff = (int)timeDiff.TotalSeconds - nStartTime;
            if (nTimeSlot <= nTimeDiff && nTimeSlot < 360)
            {
                using (System.IO.FileStream fileStream = new System.IO.FileStream(HttpContext.Current.Request.MapPath("~/data.txt"), System.IO.FileMode.Append, System.IO.FileAccess.Write, System.IO.FileShare.ReadWrite, 1024, false))
                {
                    System.Text.ASCIIEncoding encBytes = new System.Text.ASCIIEncoding();
                    while (nTimeSlot <= nTimeDiff)
                    {
                        double dSin = Math.Sin(nTimeSlot * 6.28 / 360) * 10000 + 10000;
                        int nSin = ((int)dSin);
                        byte[] bytes = encBytes.GetBytes(nSin.ToString("D") + ";" + nTimeSlot.ToString("D") + "\r\n");
                        fileStream.Write(bytes, 0, bytes.Length);
                        nTimeSlot += 1; // Set increment to 10 to see how it works when there is not so much data.
                        if (nTimeSlot >= 360)
                        {
                            fileStream.Write(new byte[] { (byte)'E', (byte)'n', (byte)'d', (byte)'\r', (byte)'\n' }, 0, 5); // Marker for end
                            nTimeSlot = -1;
                            break;
                        }
                    }
                }
            }
            return nTimeSlot;
        }
        [System.Web.Services.WebMethod(BufferResponse = false)]
        [System.Web.Script.Services.ScriptMethod()]
        public static long Reset()
        {
            System.DateTime dateTime = System.DateTime.Now;
            System.DateTime date1970 = new System.DateTime(1970, 1, 1, 0, 0, 0);
            System.TimeSpan timeDiff = dateTime.Subtract(date1970);
            using (System.IO.FileStream fileStream = new System.IO.FileStream(HttpContext.Current.Request.MapPath("~/data.txt"), System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.ReadWrite, 1024, false)) {}
            return (int)timeDiff.TotalSeconds;
        }
      
    </script>
    <script type="text/javascript">
        var g_timeSlot;
        var g_startTime;
        function Start()
        {
            PageMethods.Reset(OnStartComplete);  
        }
        function OnStartComplete(result)
        {
            var state = document.getElementById ("state");
            state.innerHTML= 0;
            g_timeSlot = 0;
            g_startTime = result;
            setTimeout(function (){UpdateData();},1000);
        }
        function UpdateData()
        {
            PageMethods.UpdateTillTimeSlot(g_timeSlot, g_startTime, OnUpdateDataComplete);
        }
        function OnUpdateDataComplete(result)
        {
            var state = document.getElementById ("state");
            state.innerHTML= result;
            if (-1 != result)
            {
                g_timeSlot = result;
                setTimeout(function (){UpdateData();},1000);
            }
        }
    </script>  
</head>
<body onload="Start();">
    <h1>ASP.NET AJAX Chart Data Updater</h1>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" />
    </form>
    <span>Timeslot: </span><span id="state"></span>
</body>
</html>

Like with the php solution the page updater.asp must be opened before the page chart.aspx can be used. 

For this ASP.NET solution ASP.NET 4.0 is necessary.

Conclusion  

I think this article shows: It is possible to show Ajax stock charts without pictures and plug-ins. And it is rather simple! The only risk is the network traffic through the Ajax connection. But this is inevitable if actual values are needed. If no actual values are needed, only static charts, then showing the charts with JavaScript and CSS instead of chart pictures can even avoid network traffic, because the JavaScript charts can be transferred with the first call of the internet page. No second http call for a chart picture is needed.

 

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ramser
Web Developer
Switzerland Switzerland
Little programmer from Switzerland

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 30 Apr 2012
Article Copyright 2012 by Ramser
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid