Combine the versatile canvas element with the robust jQuery library to create a bar graphing plugin. In this first part, we are going to code the core logic of the plugin as a standalone version.
A Word From the Author
Today, we are going to create a bar graphing plugin. Not an ordinary plugin, mind you. We'll show some jQuery love to the canvas element to create a very robust plugin.
In this two-part article, we will start from the beginning by implementing the logic of the plugin as a standalone script, refactoring it into a plugin and then finally adding all the additional eye candy on top of the plugin code. In this first part, we are going to deal solely with implementing the core logic.
Need an example before we get started? Here you go!

Different graphs created with supplying different settings to our plugin
Satisfied? Interested yet? Let's start.
Functionality
Our plugin needs to accomplish some basic things whilst not doing some other things. Let me elucidate:
- As usual, we are going to utilize only the canvas element and JavaScript. No images of any kind, no broken CSS techniques, no prerendering. Plain old (or is it new?) canvas element along with some jQuery to lighten our workload.
- With respect to the data source, we are going to pull all of the data directly from a standard table. No arrays to pass on the plugin at startup. This way the user can just put all the data in a table and then invoke our plugin. Plus, it is much more accessible.
- No special markup for the table acting as the data source and definitely no special classes names for the data cells. We are going to utilize only the ID of the table and pull all our data from there.
- No flimsy text overlay for rendering the labels and such on the graph. It is not only highly tedious but the rendered text isn't part of the graph when it is saved. We are going to use the fillText and strokeText as defined by the WHATWG specs.
Dependencies
As we are delving into the world of cutting-edge, still not fully specified, technology, we do have some dependencies. For the canvas element to work, most modern browsers are sufficient. But since we make use of the new text rendering API, we need newer builds. Browsers utilizing the Webkit engine r433xx and above or the Gecko engine 1.9.1 and above should be excellent platforms for the plugin. I recommend grabbing a nightly build of Chromium or Firefox.
Before We Start
I'd like to mention that our plugin is purely for learning purposes. This plugin is in no way meant to replace other full-fledged graphing plugins like Flot, Plotr and such. Also the code is going to be as verbose as possible. You could write far, far more efficient code but for the sake of learning, everything is going to be as uncomplicated as possible. Feel free to refactor it in favor of efficiency in your production code.
The HTML Markup
<!DOCTYPE html> <html lang="en-GB"> <head> <title>OMG WTF HAX</title> </head> <body> <table width="200" border="0" id="data"> <tr> <th>Year</th> <th>Sales</th> </tr> <tr> <td>2009</td> <td>130</td> </tr> <tr> <td>2008</td> <td>200</td> </tr> <tr> <td>2007</td> <td>145</td> </tr> <tr> <td>2006</td> <td>140</td> </tr> <tr> <td>2005</td> <td>210</td> </tr> <tr> <td>2004</td> <td>250</td> </tr> <tr> <td>2003</td> <td>170</td> </tr> <tr> <td>2002</td> <td>215</td> </tr> <tr> <td>2001</td> <td>115</td> </tr> <tr> <td>2000</td> <td>135</td> </tr> <tr> <td>1999</td> <td>110</td> </tr> <tr> <td>1998</td> <td>180</td> </tr> <tr> <td>1997</td> <td>105</td> </tr> </table> <canvas id="graph" width="550" height="220"></canvas> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="mocha.js"></script> </body> </html>
Nothing special about the markup. I'll do a quick overview anyway.
- We begin by including the requisite doctype. Since we are using the canvas element, we use the appropriate one for HTML 5.
- The data source table is then defined. Do notice that no special markup is being described or new classes being defined and assigned inside its members.
- A canvas element is defined and then assigned an ID to later reference it to. This specific canvas element will only be here for the standalone version. In the plugin version, the canvas element and its attributes will be injected dynamically into the DOM and then manipulated as needed. For progressive enhancement this way works out a lot better.
- Finally, we include the jQuery library and our custom script. As Jeffrey has mentioned time and again, including scripts at the end of the document is always a good idea.
The Canvas Grid
Before we start the Javascript, let me explain the canvas coordinate system. The top-left corner acts as the origin i.e. (0, 0). Points are then measured with respect to the origin with x increasing along the right and y increasing along the left. For the mathematically inclined, we are effectively working in the 4th quadrant except that we take the absolute value of y instead of its negative value. If you have worked with graphics in other languages you should be at home here.

The canvas co-ordinate system
The Rectangle Rendering Routine
Canvas' rectangle rendering routine will be used extensively through out the article to render the bars, the grid and some other elements. With that in mind, let's take a short look at those routines.
Of the three routines available, we will be using the fillRect and strokeRect methods. The fillRect method actually fills the rendered rectangle while the strokeRect method only strokes the rectangles. Other than that, both the methods take the same parameters.
- x - The x coordinate of the point from where to start drawing.
- y - The y coordinate with respect to the origin.
- width - Defines the width of the rectangle to be drawn.
- height - Defines the height of the rectangle.

The Javascript Magic
As always, I highly recommend you to download the source code and have it on the side for reference. It's easier to look at the big picture and parse each function one by one than look at each function individually and then create the big picture in your mind.
Variable Declaration
var
barSpacing = 20,
barWidth = 20,
cvHeight = 220,
numYlabels = 8,
xOffset = 20,
gWidth=550,
gHeight=200;
var maxVal,
gValues = [],
xLabels = [],
yLabels = [];
var cv, ctx;
Graph variables
- xLabels - An array which holds the value of the labels of the X axis.
- yLabels - Same as above except that it contains the values of the Y axis labels.
- gValues - Array which holds all the graph data we pull off the data source.
- cv - Variable to point towards the canvas element.
- ctx - Variable to refer to the context of the canvas element.
Graph Option Variables
These variables hold hard coded values to aid us in positioning and layout of the graph and the individual bars.
- barSpacing - Defines the spacing between individual bars.
- barWidth - Defines the width of each individual bar.
- cvHeight - Defines the height of the canvas element. Hard coded since we created the canvas element beforehand. Plugin version varies in this functionality.
- numYlabels - Defines the number of labels to be drawn in the Y axis.
- xOffset - Defines the space between the beginning of the canvas element and the actual graph. This space is utilized for drawing the labels of the Y axis.
- gWidth, gHeight - Hard coded values holding the dimension of the actual rendering space of the graph itself.

How each variable controls the appearance of the graph
Grabbing the Values
With jQuery's strong selector engine it becomes very easy for us to get the data we need. Here we have a number of ways to access the necessary elements. Let me explain a few below:
$("tr").children("td:odd").each(function(){
//code here
});
The simplest way to access the necessary rows. Looks for a tr element and then accesses every other td element. Fails miserably when you have more than one table on your page.
$("#data").find("td:odd").each(function(){
//code here
});
A much more straight forward way. We pass in the ID of the table and then access every other row.
$("#data tr td:odd").each(function(){
//code here
});
Same as above except that we just use CSS style selector syntax.
$("#data tr td:nth-child(2)").each(function(){
//code here
});
The version we are going to use today. This way is a lot better if we need to grab data from a different row or, if needed, multiple rows.
The final version looks like so:
function grabValues ()
{
// Access the required table cell, extract and add its value to the values array.
$("#data tr td:nth-child(2)").each(function(){
gValues.push($(this).text());
});
// Access the required table cell, extract and add its value to the xLabels array.
$("#data tr td:nth-child(1)").each(function(){
xLabels.push($(this).text());
});
}
Nothing complicated here. We use the snippet of code mentioned above to add the value of the table cell to the gValues array. Next, we do the same except that we access the first table cell in order to extract the requisite label for the x axis. We've encapsulated the data extraction logic to its own function for code reusability and readability.
Canvas Initialization
function initCanvas ()
{
// Try to access the canvas element and throw an error if it isn't available
cv = $("#graph").get(0);
if (!cv)
{ return; }
// Try to get a 2D context for the canvas and throw an error if unable to
ctx = cv.getContext('2d');
if (!ctx)
{ return; }
}
Routine canvas initialization. First we try to access the canvas element itself. We throw an error if unable to. Next up, we try to obtain a reference to the 2d rendering context through the getContext method and throw an error if we're unable to do so.
Utility Functions
Before we step into the actual rendering of the graph itself, we need to look at a number of utility functions which aid us greatly in the process. Each of them are tiny by themselves, but will be used extensively throughout our code.
Determining the Maximum Value
function maxValues (arr)
{
maxVal=0;
for(i=0; i<arr.length; i++)
{
if (maxVal<parseInt(arr[i]))
{
maxVal=parseInt(arr[i]);
}
}
maxVal*= 1.1;
}
A small function which iterates through the passed array and updates the maxVal variable. Do note that we inflate the maximum value by 10% for special purposes. If the maximum value is left as it is, then the bar representing the topmost value will touch the edge of the canvas element which we do not want. With that in mind, a 10% increment is issued.
Normalizing the Value
function scale (param)
{
return Math.round((param/maxVal)*gHeight);
}
A small function to normalize the extracted value with respect to the height of the canvas element. This function is used extensively in other functions and directly in our code to express the value as a function of the height of the canvas. Takes a single parameter.
Returning the X Coordinate
function x (param)
{
return (param*barWidth)+((param+1)*barSpacing)+xOffset;
}
Returns the x ordinate to the fillRect to aid us in the positioning of each individual bar. I'll explain this a bit more in detail when it is used.
Returning the Y Coordinate
function y (param)
{
return gHeight - scale (param) ;
}
Returns the y ordinate to the fillRect method to aid us in the positioning of each individual bar. More explanations a bit later.
Returning the Width
function width ()
{
return barWidth;
}
Returns the width of each individual bar.
Returning the height
function height (param)
{
return scale(param);
}
Returns the height of the bar to be drawn. Uses the scale function to normalize the value and then returns it to the caller.
Drawing the X Axis Labels
function drawXlabels ()
{
ctx.save();
ctx.font = "10px 'arial'";
ctx.fillStyle = "#000";
for(index=0; index<gValues.length; index++)
{
ctx.fillText(xLabels[index], x(index), gHeight+17);
}
ctx.restore();
}
A simple function to render the labels of the x axis. We first save the current state of the canvas including all the rendering settings so that anything we do inside the functions never leaks out. Then we set the size and font of the labels. Next, we iterate through the xLabels array and call the fillText method each time to render the label. We use the x function to aid us in the positioning of the labels.
Drawing the Y Axis Labels
function drawYlabels()
{
ctx.save();
for(index=0; index<numYlabels; index++)
{
yLabels.push(Math.round(maxVal/numYlabels*(index+1)));
ctx.fillStyle = "#000";
ctx.fillText(yLabels[index], xOffset, y(yLabels[index])+10);
}
ctx.fillText("0", xOffset, gHeight+7);
ctx.restore();
}
A slightly more verbose function. We first save the current state of the canvas and then proceed. Next we divide maxVal's value into n elements where the variable numYlabels dictates n. These values are then added to the yLabels array. Now, as shown above, the fillText method is called to draw the individual labels with the y function aiding us in the positioning of each individual label.
We render a zero at the bottom of the canvas to finish drawing the Y labels.
Drawing the Graph
function drawGraph ()
{
for(index=0; index<gValues.length; index++)
{
ctx.save();
ctx.fillStyle = "#B7B7B7";
ctx.fillRect( x(index), y(gValues[index]), width(), height(gValues[index]));
ctx.restore();
}
}

The function which draws the actual bars in the bar graph. Iterates through the gValues array and renders each individual bar. We use the fillRect method to draw the bars. As explained above, the method takes four parameters, each of which is taken care of by our utility functions. The current index of the loop is passed to our functions as parameters along with the actual value held in the array, as needed.
The x function returns the x co-ordinate of the bar. Each time, it is incremented by the value of the sum of barWidth and barSpacing variables.
The y function calculates the difference between the height of the canvas element and the normalized data and returns it. I know this sounds a bit topsy turvy, but this is due to the fact that the y values on the canvas grid increase on moving down while in our graph the y values increase on moving up. Thus, we have to do a little work to make it function the way we wish.
The width function returns the width of the individual bars themselves.
The height function just returns the normalized value which is going to be used as the height of the bar to be drawn.
Summary
In this first part, we've implemented the base logic of our plug-in as a standalone version with bare bones looks and features. We reviewed the canvas coordinate system, the rectangle rendering methods, some nifty data extraction using jQuery's innate awesomeness [Have I mentioned how much I like jQuery?], looked at how the labels are drawn, and finally looked at the logic behind the rendering of the graph itself.
At the end of this article, the output should look like so.

Next Up!
Our current implementation is rather lacking really. It looks bland, can't create multiple graphs on the same page, and let's face it, is rather spartan on the features front. We are going to tackle all of that next week. In the next article we will:
- Refactor our code towards making it a full-fledged jQuery plugin.
- Add some eye candy.
- Include some nifty little features.
Questions? Criticisms? Praises? Feel free to hit the comments. Thanks for reading and stay tuned for the next part!
- Follow us on Twitter, or subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.
Related Posts
Check out some more great tutorials and articles that you might like
Plus Members
Source Files, Bonus Tutorials and
More for $9 a month for all TUTS+
sites in one subscription.











User Comments
( ADD YOURS )g_alex_stef June 5th
great tut man, i ll give it a try
( )Michael June 5th
wow, really nice. perfect timing, need it for my next project
( )Milan June 5th
Very nice, quite useful for a couple sites I know.
( )bill June 5th
Nice but use Flash for this instead. Sadly the majority of web surfers (e.g. IE 6 & 7) can’t view this without hacks so why bother?
( )Emil Hajric June 5th
Bill, you do have a point. But, I hate Flash on a site more than I hate hacking IE code!
( )Aaron June 5th
All depends on your target audience. Maybe its to a point where we developers have to abandon these old broken browsers
( )Andrei August 11th
I can’t view flash on my iPhone.
( )Morten Najbjerg June 5th
Great and useful article. I’m looking forward to part two…
( )Noel Tiangco June 5th
great tutorial. if other readers are looking for a graphing tool now, check out http://code.google.com/p/flot/
( )rishteria June 5th
So useful article, nice for share it
( )DynamicGuru June 5th
Hmmm…. I may say nice tut but i didnt get the canvas element, is it some new kinda element???
( )Anyway thnx for the tut
Crysfel June 5th
it is not new… but a few people knows about it
( )Roger Stringer June 5th
ctx.fillText doesn’t work with some versions of FF3, you have to have a fix for it
For example, for the drawXLabels function, instead of just having:
ctx.fillText(xLabels[index], x(index), gHeight+17);
You have to use this:
if(ctx.fillText) {
ctx.fillText(xLabels[index], x(index), gHeight+17);
} else if(ctx.mozDrawText) {
ctx.translate(x(index), gHeight+17);
ctx.mozDrawText(xLabels[index]); // for FF3-
ctx.translate( (0-x(index)), (0-(gHeight+17)) );
}
Just to cover in case of fail, otherwise, you’ll get error messages that ctx.fillText doesn’t exist.
It’s dumb, but unfortunate.
( )adam June 5th
the source doesn’t work. javascript error in IE8
( )Myfacefriends June 5th
very nice tuts keep up the good work dudes!
( )Dario Gutierrez June 5th
I something new for me! Excellent tut.
( )Neil June 5th
wow, such a lot of work for a static graph, that doesn’t render in Firefox!
I prefer using Flash – send a XML document generated by SQL to render an interactive Graph
( )Siddharth June 5th
This is more of a fun learning exercise than a real world project actually. For actual projects, I highly recommend using SVG rather than Flash.
( )bill June 5th
I don’t. Flash is found in over 98% of browsers. Canvas isn’t.Plus doing this is Flash is far easier and more extensible.
s June 7th
@bill : – some dont have flash though
Patrik Ilola June 5th
Very nice! Actually need it for my current project. Right now I’m using flash to render the graphs, but… I don’t like it
( )paradox June 5th
Very cool !
Thanks
( )Akshay Sura June 5th
This is why Microsoft is adopting jQuery and integrating it into Visual Studio.
( )Moksha June 5th
nice thanks for sharing
( )astroot June 5th
The Google Chart API is a great tool for making a quick graph.
( )sarmen June 5th
thanks for sharring. i noticed that the demo file doesnt work in firefox 3, internet explorer 8, and partly displays someting in google chrome. what browser are you using?
( )Thomas Milburn June 6th
Very nice tutorial! I’m looking forward to the sequel. There’s only one problem for me and that is the Y axis labels, I don’t like this line of code
yLabels.push(Math.round(maxVal/numYlabels*(index+1)));I would use:
yLabels.push(Math.round(maxVal/numYlabels)*(index+1));so the difference between each value is the same but what if the values in the chart are below 1?
It is even better if the numbers are multiples of 2, 5 or 10 in which case a more complex function is needed.
( )Jeff June 6th
no demo?
( )booyah June 6th
Nettuts is for NUTS.
( )Taylor Satula July 13th
Are you saying that as a good thing or a bad thing?
( )samuel pushpak June 6th
wonderful!! very useful.
mr.siddharth.. can you please give your email id.
( )Javascript Guy June 7th
wow, what an awesome tutorial. I can’t wait for the coming one next week!
( )Jozko June 7th
Amazing… very good tutorial. I’ll implement this plugin on one of my sites. Thanx.
( )Carl - Web Courses Bangkok June 7th
Wow, sure that nettuts reads minds, as we are about to do a web site that would benefit from this. Nice to see some international developers stepping up
Great tutorial thank you Siddharth
( )wit June 13th
Amazing… very good tutorial. I’ll implement this plugin on one of my sites. Thank you very much
( )phoenixfly June 23rd
ultra nice tutorial.. this is what I looking for months.. and fnally I found it here. good work brother! thx a bunch ^_^
( )Phil D August 7th
Cool. I usually use ChartGo to create a graph online and then save the image. But when I need to use Jquery, this tutorial is perfect.
( )Yariv September 28th
Great work!!
( )Is there a way to Draw the text Vertically?
Will November 4th
So I’ve searched a few times on here and I can’t find part 2. Has that been written yet?
( )Siddharth November 4th
Yes. It just hasn’t been published yet.
( )ITALO COSTA November 6th
PT-BR-> Olá, como faço para salvar a imagem gerada no
EN-US-> Hi, How do I do to save img generate in
Obrigado
( )Thankyou