Page 1 of 1

XML Grid Convert an XML file to normal or pivot grid Rate Topic: -----

#1 ahmad_511   User is offline

  • MSX
  • member icon

Reputation: 131
  • View blog
  • Posts: 722
  • Joined: 28-April 07

Post icon  Posted 04 October 2009 - 03:10 PM

Hello all,
I’ve created this grid about one year ago to do one small thing which is converting supplied XML file into a grid view, with hard coded styles names inside.
About couple of months ago, I’ve started another project that needs more flexibility and customizations while building the grid,
So, I fixed some displaying bugs and enhanced it a little bit.
Currently I’m using it in about 8 pages (some of them have 4 grids) and till now everything went well.
And after spending fairly good time in testing, I would like to share it with you hoping that you’ll find it useful.

In This tutorial we will create our own XML Grid (simple but useful one),
I was thinking to post it as a code snippet, but I changed my mind; because Tutorials let you have a general view about the code and the ability to edit it as required. (I’m not thinking about Kudos at all, I really don’t :), I swear… :D )

Before you start:
It’s recommended that you have a:
1- Basic skills in HTML (TABLE structure)
2- Basic skills in CSS
3- Basic skills in XML/XML DOM (creating well formed xml document and using firstChild/childNodes…)
4- Intermediate skills in Javascript (objects, HTML/XML DOM)

In order to load the XML, we will use an AJAX code snippet (attached) to load the XML document to the client, and since AJAX is not the subject matter here, I’ll just mention how to use that snippet.
var ajx =new Ajax();
ajx.Method="GET"//  or "POST";
ajx.ResponseHandler=function (responseObject){
// we should get responseObject as xml document here

1- Create normal/pivot grid base on the loaded XML document.(normal/pivot have almost the same idea, but changing the way we’re reading the XML might be confusing in some places)
2- Controlling the grid style (headers, rows, hover, selected, loading message).
3- The ability to hide columns, column’s headers, row’s headers.
4- Having some useful events (onRowClick, onRowDblClick, onRowOver, onRowOut, onBindComplete).
5- The ability to pass cells contents to external function in order to apply additional checks (formating/ validating)
6- Auto generating columns headers, or passing customized headers.

Note: the XML file we will use must be in this form (the tags names shown below are not mandatory):

Important: white spaces can be read as a text node by some browsers and ignored by some other and we’re not going to do anything with that here, so we’re assuming that white spaces must be removed on the server-side (or on the client-side) before it assigned to the dataSource property,
That’s why the previous xml will look like this:

flow charts:
chart1:Attached Image
chart2:Attached Image

Creating main function:
First, we have to create the needed properties and give them default values.
function XMLGrid(){

this.dataSource=null;//XML Object
this.type="normal";// or "pivot"
this.headers="auto";// or string Array of headers names
this.displayColumnsHeaders=true;//read write
this.displayRowsHeaders=true;//read write
this.selectedRow=null;// Object
this.appendTo=document.body;// default parent element
this.checkContent=null;//Function reference
this.gridStyle=null;//class name
this.columnHeaderStyle=null;//class name
this.rowHeaderStyle=null;//class name
this.rowStyleArr=null;//Array of class’s names
this.hoverStyleArr=null;//Array of class’s names
this.selectedStyleArr=null;//Array of class's names
this.hiddenCols=null;//comma separated of column's names
this.gridRef=null;// inner use + reference to the table
this.tempheaders=this.headers;// inner use to temporary holds the header's name.
this.loadingMessage="Loading...";// shows loading message
this.emptySourceMessage="No records have been found";// shows empty source message

var me=this;// refers to the XMLGrid inside inner functions

Within the main function XMLGrid() we’ll be dealing with 4 other sub functions:
1- bindFrom(): uses AJAX to communicate with a server side script that generates the XML on fly (or load already created XML file). After loading XML the refresh() function will be called.
2- refresh(): removes the grid if it’s already exists, call createGrid() function and then append the new created one.
3- createGrid(): creates normal/pivot grid according to the declared properties. Call addRowEvents() for each row in the grid.
4- addRowEvents(): adds the necessary styles/events to the passed row.

Function: bindFrom(path,data,sendMethod):
This function must be called after assigning values to all necessary properties (styles, events…).
path: is the path to the XML file or the server side script that generates the XML file.
data (optional):name/value pairs to be sent to the server side script.
sendMethod (optional, default is GET): GET/POST, the method used to post data.
Note: don’t use POST method (even if you have nothing to post in the data argument) if you’re loading a pre-created XML file which is physically exists on the host.

This function uses AJAX function I mentioned before, so first we check if this function is available, and notify user if not.
The random number we added to the name/value pairs used to get rid of some browser caching issue (when using GET).
Since we want to load an XML document, we should then set the AJAX ResponseFormat property to "xml"
Before sending an AJAX request we create a div element to hold the loading message, adding its style if defined and append it to the grid parent element

When AJAX calls back the response function, we remove loading message, assign the returned xml object (xmlObj) to the XMLGrid.dataSource property and call refresh() function
	if (Ajax){
		var ajx =new Ajax();
		ajx.Data="rnd=" + Math.floor(Math.random()*1000);
		if (data!=undefined)ajx.Data+="&"+data;
		ajx.ResponseHandler=function (xmlObj){
		// create div to display the messaage
		var lm=document.createElement("div");
		alert("You have to include AJAX in order to use bindFrom method")

Function: refresh():
Remove the grid if the gridRef property is already assigned.
Call createGrid() and assign its return to the gridRef property. This is helpful when we want to call bineFrom() multiple times, otherwise the new call to bindFrom() will add a new grid under the last created one.
	if (me.gridRef!=null)me.appendTo.removeChild(me.gridRef);

Function: createGrid():
Reset the selected row property
Check if dataSource property is assigned and contains data, if not display the emptySourceMessage.
Add commas to the left and right of hiddenCols string to facilitate detecting column’s name in order to hide it.
Assign a new Array with one empty string element to the rows styles arrays if it’s not set properly.
Assign all xmlObj’s nodes those already stored in the dataSource property to the xml variable.
Create the table and table-body element
Assign the first row nodes to xmlfields variable to be used to generate the column’s headers if not specified. (Bases on node’s names)
Add the grid style if declared


if(!me.dataSource || !me.dataSource.firstChild || me.dataSource.firstChild.childNodes.length==0){
	return d;

if (me.hiddenCols!=null){

// check row style
if (me.rowStyleArr ==null || typeof(me.rowStyleArr)!="object")me.rowStyleArr =new Array("");
if (me.rowStyleArr.length==0)me.rowStyleArr =new Array("");

// check hover style
if (me.hoverStyleArr ==null || typeof(me.hoverStyleArr)!="object")me.hoverStyleArr =new Array("");
if (me.hoverStyleArr.length==0)me.hoverStyleArr =new Array("");

// check selected style
if (me.selectedStyleArr ==null || typeof(me.selectedStyleArr)!="object")me.selectedStyleArr =new Array("");
if (me.selectedStyleArr.length==0)me.selectedStyleArr =new Array("");

var xml=me.dataSource.firstChild.childNodes
var tbl=document.createElement("table")
var tblbody=document.createElement("tbody")
var xmlfields=xml[0].childNodes

if (me.gridStyle!=null)tbl.className=me.gridStyle;

Create headers:
Check if tempheader (the instance of headers property) is declared, if so, use the tempheader to create the grid header, if not create the headers base on the node’s names of xmlfields.
if (typeof(me.headers)!="object"){
    me.tempheaders=new Array();
    for (i=0;i<xmlfields.length;i++){
    me.tempheaders=me.headers;// reset the headers

Create Normal table:
Normal grid reading direction:Attached Image

Check if type property is equals to "normal"
Check if displayColumnsHeaders is set to true
Create the first row element TR to hold columns headers cells
Create the headers cross cell TH and append it to the TR just if both of row’s headers and column’s headers are set to be displayed.
if (me.type=="normal"){  
if (me.displayColumnsHeaders){
var tr= document.createElement("tr")
if (me.displayColumnsHeaders && me.displayRowsHeaders){
    var th=document.createElement("th")

Loop the elements in the headers array, and create header cell for each one.
Check if the header’s name exists within the comma separated hiddenCols property, if so; change its display to “none”
Append the header cell to the header row
Add the column’s headers style if declared
Append the header row to the table’s tbody
    // hide mentioned columns

if (me.columnHeaderStyle!=null)tr.className=me.columnHeaderStyle;

Creating rows:
Loop the rows in the xml object and create TR element for each one
Pass the TR element to the addRowEvents(), will be discussed later.
Get the child nodes of each xml row and assign it to the row variable.
Check If displayRowsHeaders property is set to true
Create TH element for each row.
Add the row number to the TH using the loop counter ®.
Add the row header style if declared.
Append the header cell to the row element
      addRowEvents(tr);// Adding Events
      var row=xml[r].childNodes
      // Adding rows headers
      if (me.displayRowsHeaders){

Creating cells:
Loop the row’s elements and create TD element for each one
Get cell’s data, depending on the browser, it may be the text or the textContent property, anyways; using logical or (||) will return the defined one.
If checkContent function is defined, we pass the cell’s data with the current row, column indexes, the column’s name and the TD object itself to that function and assign its return to data again.
Decode the content of the cell’s date in case it contains some html elements / encoded contents and add this content to the TD element.
Add onmouseover event to the TD in order to get the column’s index which is the same as the cell’s index within its parent row.
Check if the current cell belongs to one of the hidden columns; if so change its display property to “none”
Append TD cell to the parent row and the parent row to the table-body
        for (c=0;c<row.length;c++){
            var data=row[c].text || row[c].textContent
            if (data==undefined)data=""
            if (me.checkContent!=null)data=me.checkContent(decodeURIComponent(data),r,c ,me.tempheaders[c],td)


            // hide mentioned columns

Create Pivot table:
pivot grid reading direction:Attached Image
Check if type property is equals to "pivot"
Check if displayColumnsHeaders is set to true
Create the first row element TR to hold columns headers cells
Create the headers cross cell TH and append it to the TR just if both of row’s headers and column’s headers are set to be displayed.
if (me.type=="pivot"){

// Adding columns headers
if (me.displayColumnsHeaders){
tr= document.createElement("tr")
if (me.displayColumnsHeaders && me.displayRowsHeaders){

Loop the xml rows, and create header cell for each one.
Add the row number to the column’s header cell (since it’s pivot table, the row’s header will become the column’s header.
Add the column’s headers style if declared.
Append TR header row element to the table-body.

if (me.columnHeaderStyle!=null)tr.className=me.columnHeaderStyle;

Creating rows:
Create a new property (hiddenCount) for the addRowEvents() function to hold the processed hidden columns count to add the correct rowStyle/alternativeRowStyle property to the columns (it’s rows when we’re talking about pivot).
Loop xmlfields elements (which contains the first row element in the xml document)

Note: Since we’re creating a pivot table, all columns will become rows now. Also we should hide rows instead of columns in this case.
Create a new TR element for each element in xmlfields
If the row header belongs to the hiddenCols, set the TR display to “none” and increase thehiddenCount
Call addRowEvents()
If displayRowsHeaders is set to true, create TH element, add the header text to it, add the row header style if declared.
Append TH to TR.
for (r=0;r<xmlfields.length;r++){
        // hide mentioned columns (rows here)
            if(me.hiddenCols.indexOf(","+me. tempheaders [r]+",")>-1){
        addRowEvents(tr);// Adding Events
        // Adding rows headers	
        if (me.displayRowsHeaders==true){
th.innerHTML=me. tempheaders [r]

Creating cells:
Loop xml rows and create table cell for each one
Here we will read vertically from the xml, for example the first cell in each row then the second…
Now, just like the normal table:
We Get the cell data and pass it to the checkContent() function if declared and pass it the current row, column indexes , the column’s name and the TD object itself.
Decode the data then add it to TD element
Change the column property when mouse over a cell
Append the cell to its parent row and the parent row to the table-body.

            data=xml[c].childNodes[r].textContent || xml[c].childNodes[r].text
            if (data==undefined)data=""
            if (me.checkContent!=null)data=me.checkContent(decodeURIComponent(data),r,c ,me.tempheaders[r], td)
            // hide mentioned columns

Finalizing the table:
Remove cell spacing
Append tbody to the table
Call onBindComplete() function if declared and pass the table object to it so you might want to do something with it before it appears on the page.
Return the table object to the caller function which is refresh() to be assigned the result to gridRef property
return tbl

Function: addRowEvents(tr):
Create a new variable (deduct) and set it to 0
Now, if you still remember we change the hiddenCount property before we call addRowEvents() while creating a pivot table.
Deduct variable will be increased as rows added (in pivot table rows are columns, so hidden columns will be hidden rows, and this may causes us a styling problems for rows and alternative rows styles)
So, if there are hidden columns we always deduct them from the actual rows count to get the viewable rows count.
After that we can add the rowStyle according to the rowStyleArr and the number of the row
Also, we add 3 new attribute (originalStyle, selectedStyle, hoverStyle) to the passed row according to its number, in order to use them later when we mouse out, click and hover a row.
function addRowEvents(tr){
var deduct=0

tr.className=me.rowStyleArr[(r -deduct) % me.rowStyleArr.length];


tr.setAttribute("selectedStyle",me.selectedStyleArr[(r -deduct) % me.selectedStyleArr.length])

tr.setAttribute("hoverStyle",me.hoverStyleArr[(r -deduct) % me.hoverStyleArr.length])

Event: onclick ():
Turn back the orginal row style for the selected row (the last one)
Change selectedRow property to point to the current selected row
Apply the selectedStyle if defined
Call onRowClick function if declared, and pass it the current row/column indexes and the row object itself.
if (me.selectedRow!=null){

if (this.getAttribute("selectedStyle")!="")this.className=this.getAttribute("selectedStyle");

if (me.onRowClick!=null){

Event: ondblclick ():
If onRowDblClick() function declared, change the selectedRow property to the current row, onRowDblClick() and pass it the current row/column indexes and the row object itself.
if (me.onRowDblClick!=null){

Event: onmouseover():
Change the row property
Apply hover style if declared
Call onRowOver() if declared.

	if (this.getAttribute("hoverStyle")!="")this.className=this.getAttribute("hoverStyle");


Event: onmouseout():
If hoverStyle is defined, apply the original row style
Apply the selected row style if we moved the mouse out of the selected row.
Call onRowOut() function if defined.
		if (this==me.selectedRow && this.getAttribute("selectedStyle")!=""){

Note: it doesn’t matter if you want to use your own AJAX code, you already have the xml object or you did some changes to the XML on the client side so you don’t need to load it again, simply do the following:
- forgot about bindFrom() function.
- load the XML object using any way you’re comfortable with
- assign the XML to the dataSource property
- call refresh() method.

How to use it:
First, load ajax.js (if you want to use attached AJAX), xmlGrid.js on the page header
Make sure the page is already loaded the xmlGrid.js file, in another words put your code inside a function and then call it on page load event onload().
<script type="text/javascript" src="ajax.js"></script>
<script type="text/javascript" src="xmlGrid.js"></script>

<script type="text/javascript">
function init(){

// your code here

<body onload="init()">

1- The Minimum usage can be done by calling creating a new instance of the XMLGrid() and call bindFrom() method specifying the path to xml file (or server-side script that generates xml file)
This will load test.xml file and append it to the page body directly.
var g=new XMLGrid();

If you already have the xml object, you want to use your own AJAX or you build the xml object dynamically inside javaSctipt you can careate an instabce of XMLGrid(), set the dataSource property to xml object you have and then call refresh() method
var g=new XMLGrid();

2- Append the grid to an existing element the can accept a table element to be appended to (div, form...)
Assuming that you have a div element with id="dvXGrid"
var g=new XMLGrid();

Once you call bindFrom() method the grid will start processing and creating XMLGrid assigned properties, so any property assigned after that will be useless, but it will appears if you call refresh() method or call bindFrom() again

Minimum use/Normal grid:Attached Image

3- Pivot grid can be created by setting type property to "pivot" , (by default "norma")
var g=new XMLGrid();

Minimum use/Pivot grid:Attached Image

4- Adding custom columns headers (rows headers n case of using pivot type), (by default "auto")
g.headers=new Array("C1","C2","C3","C4");

We used an Array() object to pass the headers names.

customized headers:Attached Image

5- Hiding columns/rows headers

6- Styling, specify the class name related to each property.
Please consider the following:
gridStyle: styles the table object
columnHeaderStyle:styles to the row element that contains the header cells, if you want to apply a style to the header cells themselves use (th) selector.
Xgridrowheaderstyle: styles the header cell in each row.
rowStyleArr:styles the rows elements according to the row’s number, you can use (td) to target the cells within a row.
hoverStyleArr: styles the row element under the cursor according to the hovered row’s number,.
selectedStyleArr: styles the clicked row element according to theselected row’s number,.
loadingMessageStyle: styles the loading message.
emptySourceMessageStyle: styles the returned message if xml file was empty or no not passed

g.rowStyleArr=new Array("xgridrowstyle","xgridalternativerowstyle");
g. hoverStyleArr= new Array("xgridhoverstyle";)/>
g.selectedStyleArr=  new Array("xgridselectedrowstyle");

For rowStyleArr, hoverStyleArr and selectedStyleArr, you can add as many as you want of class’s names

Styled grid:Attached Image

7- Changing loading/empty source messages
g.loadingMessage="Please wait";

8- Hidden columns, pass wanted columns header’s names as a comma separated string

9- selectedRow returns the selected row element TR, you can use the (cells) collection to get the data from a specific cell inside selected row. Declare the grid object (g) directly after the script tag.
Var cell1= g.selectedRow.cells[1].innerHTML;

In order to deal with the grid object outside the function where it was created; make sure you assign it to a variable that can be accessed from where you want to use it.

10- checkContent, reference /anonymous function, this function accepts 4 arguments (data, row’s index, column’s index, column’s name and the cell TD) (row’s name in case of pivot grid).
The following encloses all data in the column “C1” with “***”.
g.checkContent=function (d,r,c,cn,cellObj){
if (cn=="C1") {
return "***"+d+"***"
 return d

You can also check for rows/columns indexes
Notes: the headers are counted in the indexes.
You must always return something.

11- onRowClick, onRowDblClick, onRowOver, onRowOut: all of these are references / anonymous functions, and they accept 3 arguments (row’s index, column’s index, row element TR)
g.onRowClick=function (r,c,obj){

12- onBindComplete, reference / anonymous function, accepts one argument (table element) and it’s called after the grid is created but before it’s appended to its parent. This is useful when you want to do some modification to the grid or calling another thing after the grid has been completely created but not yet included into the page.

13- Accessing the table’s elements from outside script which is not related to an event, usually we’ll use the element’s (id), and as you already know we didn’t add this attribute to our table, but if you noticed that we keep a reference to the table assigned to the (gridRef) property.

Note: make sure you run the page on a webhost or a localhost (a security issue may prevents you from opening AJAX connection directly to your local drive)

To run the attached example, make sure ajax.js, xmlGrid.js and index.htm are in the same folder

The example contains:
A button that access the table object using gridRef property and reads the grid rows count after binding is completed (including the header)
we bind the first grid from existing xml file test.xml
Use onBindComplete to call another function afterBind()
inside afterBind() we use the same dataSource to create a new grid with different style parameters, and use checkContent() function to highlight a specific cell accourding to its value and the column's name
Again we use the same dataSource to create a simple grid and use checkContent() function to add checkboxes into the first column depending on the column's name

example:Attached Image

Any suggestions or comments are welcome.
Thanks for your patience.

Attachments: (ajax.js, xmlGrid.js): Attached File (3.68K)
Number of downloads: 596 (index.htm, test.xml, style.css, header.png): Attached File (5.03K)
Number of downloads: 615

Is This A Good Question/Topic? 0
  • +

Replies To: XML Grid

#2 born2c0de   User is offline

  • printf("I'm a %XR",195936478);
  • member icon

Reputation: 187
  • View blog
  • Posts: 4,673
  • Joined: 26-November 04

Posted 05 October 2009 - 05:37 AM

Great Job. :^:
Was This Post Helpful? 0
  • +
  • -

#3 ahmad_511   User is offline

  • MSX
  • member icon

Reputation: 131
  • View blog
  • Posts: 722
  • Joined: 28-April 07

Posted 05 October 2009 - 10:23 AM

Thanks for the compliments and usual supports :)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1