Wednesday, December 9, 2009

Customizing jqPlot stacked bar chart

Recently, because of a need of a project, I have been introduced to jQuery and jqPlot. jQuery is a popular javascript library that is very powerful. jqPlot is an extension to jQuery and it adds the graph or chart capability to the library. Both are open source library and can be freely used in any projects. You can find the link to both open source project at the end of this article. The jQuery version used for my project is v1.3.2 and for jqPlot is 0.9.6.

jqPlot provides many types of charts and features. There are many ways you can customize the charts. For example, you can customize the legends, the colors, both the y-axis and x-axis and etc. You can see the example of all the available charts on jqPlot website.

However, there is one missing feature in the stackedBarChart that I need to use in my project. There is no way you can click on a "bar" in the chart that will send some request back to the server so that the screen can be updated. For example, in a weekly chart that show each days as a "bar", if I click on the bar, I want the screen to be updated and show the chart of the day. This feature is missing and I will need to add some javascript code to make it work.

There are few challenges in order to achieve this. First, I will need to detect whether a mouse click is indeed inside of a "bar". Then, when the mouse click is inside of a "bar", I need to be able to retrieve some information (like the date of the day) to send back a request to the server.

In order to detect the mouse click on the chart, I will need to add a event listener first. Here is the code:


$(function(){
var xAllowedRange = 0.4;
$.jqplot.eventListenerHooks.push(['jqplotClick', myClickHandler]);
});


The "myClickHandler" is a callback function that will be called when users click on the chart. Inside of this function, we will determine whether the mouse click is inside of a "bar" and what to do with it.

The "xAllowedRange" is a variable used to detect the "width" of the "bar". The exact middle position between 2 bars will be "0.5". So, for normal bar's width, 0.4 will be a good value. You will need to change this value based on the width of your bar. You can set the width of the bar when you create the chart.

The function "myClickHandler" is listed below:


function myClickHandler(ev, gridpos, datapos, neighbor, plot) {
var items = plot.data;//Get the chart's data from the 'plot' object
//The data for the stackedbarchart is an array of array
var itemLength = items.length;//This should be equal to number of category you have; or the number legend.
var numberOfBars = 0;//How many bars are on the chart
if (itemLength > 0) {
numberOfBars = items[0].length;
}
if (numberOfBars == 0) //no data
return;
var timeIndex = Math.round(datapos.xaxis);

//we are trying to detech which bar the user click on
//The timeIndex will give us the indication, timeIndex=2 for bar #2 and etc...
if (isInsideBar(datapos.xaxis,datapos.yaxis,items,xAllowedRange)) {
//Send a request to the server
if (numberOfBars == 24) {//Daily chart
//Change the timePeriod type to Hourly
getChart("Hourly",timeIndex);
} else {//Weekly or Monthly Chart
//Change the timePeriod type to Daily
getChart("Daily",timeIndex);
}
}
}

The function "isInsideBar()" is a function that detect whether a mouse click is inside of a bar. It is listed here:

function isInsideBar(xPos,yPos,items,allowed) {
var roundedXPos = Math.round(xPos);
if (roundedXPos == 0) {//the click happen near the y-axis
return false;
}
var result = xPos - roundedXPos;
var xIndicator = Math.abs(result);
var idx = roundedXPos-1;//since the array is zero based, we need to minus 1
var barTopValue = 0;
for (var i in items) {
barTopValue += items[i][idx];
}

var yIndicator = Math.round(yPos);
if ((xIndicator < allowed) && (yIndicator <= itemTotal)) {
return true;
}
return false;
}


The css for formatting the code snippets is from here. Thanks.

Resources:

The best most up-to-date book about jQuery is "Learning jQuery 1.3". You can check out others review on amazon website.

2 comments:

  1. Hello, what must be the value of the variable "itemTotal"?

    ReplyDelete
  2. ok, stupid question... forget it.

    ReplyDelete