[an error occurred while processing this directive]
> developer > web app development
Reduce Bandwidth Usage with Client-Side Document Rendering
by Soren Glasius, Field Application Engineer, Novell
Date Created: 2001-05-09 17:57:00.000
  Introduction
  Conclusion
introduction
Mark-up languages such as HTML can become unwieldy when used to create complex layouts. For example, when creating a table that contains a large amount of uniform data, but displays many small graphics elements-each using an alternate image tag (e.g. descriptive text), status bar display hint, and URL link-repeated HTML can quickly fill the pages. These repeats create ever-larger HTML files, which of course take up more bandwidth.

By letting the client render the HTML using JavaScript, the size of the HTML source transported over the network can be dramatically reduced.

Consider the following example in which I created a calendar that users could check to see whether a specific date was available. The original page was around 70K, and required reloading with each visit in order to provide up-to-date information. Also, the calendar was presented in two ways, one for online viewing and the other in a printer-friendly format.

For my first attempt at creating this calendar, I created static pages from a small application, and uploaded them to a web-server. In this case, the data was no more recent than the last upload.

On my second attempt, I used Java Server Pages (JSP) and a database. Now the most current information was online, but it was still slow since the size of each calendar cell was almost 300 bytes.

For my third attempt, I once again used JSP, but this time, I let the client render the HTML output. Because each element contained a lot of data that needed to be presented in clear format, but most of that information was a repeat, I figured that by letting the browser generate the output, I could considerably reduce the size of the file transferred over the network.

In choosing my data structure, I considered the following data criteria:
  • It must be easy to process.
  • It must be re-usable on several pages.
  • It must hold just enough information for the client to produce the desired output.

I used a multidimensional array to hold the year, month, and cells for each month. Each cell in the calendar was described with only two values: the date, which was set to '0' for cells before the first and after the last day of a month; and the status, which was set to '0' for past dates, '1' for available dates, and '2' for dates already booked. This reduced the amount of data used to present a single cell to 6 bytes. Since the data was reusable, some of the code for rendering it was reusable as well. The JavaScript used for this specific project is listed below.

The first step was to create a JSP that would output an array as follows: (Only March and April 2001 are shown here for simplicity.)


var array =

	[2001,			// (The year)

		[2,			// (The month)

			[

				[[0,0],[0,0],[0,0],[1,0],[2,0],[3,0],[4,0]],	// (A row in a month)

				[[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,0]],

				[[12,0],[13,0],[14,2],[15,1],[16,1],[17,2],[18,2]],

				[[19,1],[20,1],[21,1],[22,2],[23,1],[24,2],[25,2]],

				[[26,2],[27,1],[28,1],[29,1],[30,2],[31,2],[0,0]]

			]

		],

		[3,

			[

				[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[1,2]],

				[[2,1],[3,1],[4,1],[5,2],[6,2],[7,2],[8,1]],

				[[9,1],[10,2],[11,2],[12,1],[13,1],[14,2],[15,1]],

				[[16,1],[17,2],[18,1],[19,1],[20,2],[21,2],[22,1]],

				[[23,1],[24,1],[25,1],[26,1],[27,2],[28,2],[29,2]],

				[[30,1],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]

			]

		]

	];



The array was then parsed with the following functions:


function parseYear(yearArray) {

	// First element in the array is the year.

var year = yearArray[0];

// The following is month arrays

	for(m=1;m<yearArray.length;m++) {

		parseMonth(yearArray[m],year);

	}

}



function parseMonth(monthArray,year) {

	// First element in the monthArray is the month number

	var month = monthArray[0];

	document.write("<div id='kal'>");

	document.write("<a name='" + months[month] + "'></a>");

	// Second element in the monthArray is the month table

	parseTable(monthArray[1],year,month);

	document.write("</div>");

}



function parseTable(tableArray,year,month) {

	document.write("<table id='kal' border='1' ");

document.write("cellspacing='0' cellpadding='0' width='100%'>");

	document.write("<caption>"+ months[month] + " " + year + "</caption>");

	document.write("<tr align='middle' valign='middle'>");

	for(th=0;th<7;th++) {

		document.write("<th width='14%'>"+days[th]+"</th>");

	}

	document.write("</tr>");

	// The number of rows in the tableArray is not fixed.

	for(tr=0;tr<tableArray.length;tr++) {

		parseRow(tableArray[tr],year,month);

	}

	document.write("</table>");

};



function parseRow(row,year,month) {

	// Each row contains sevel cells

	document.write("<tr align='left' valign='top'>");

	for(td=0;td<7;td++) {

		parseCell(row[td],year,month);

	}

	document.write("</tr>");

}



function parseCell(tc,year,month) {

	document.write("<td>");

	// A cell can either be blank (e.g. has a value of 0)

	if (tc[0] == 0) {

		kalBlank();

	} else {

	// Or it can have a status set

		switch(tc[1]) {



		case 0:

			kalBeforeToday();

			break;

		case 1:

			document.write(dayFormat(tc[0])+": ");

			kalFree(year,month,tc[0]);

			break;

		case 2:

			document.write(dayFormat(tc[0])+": ");

			kalBusy(year,month,tc[0]);

			break;

		}

	}

	document.write("</td>");

	return docCell;

}



function dayFormat(day) {

	if (day < 10)

		return "0"+day;

	else

	return day;

}



The above code was then placed in a separate file, and reused in several places. (Parsing the array can be done in one function, but in this case several functions were used for clarity.)

To make the above code run, the following functions were used: kalBlank(), kalBeforeToday(), kalFree(), and kalBusy(). These functions were the only ones needed to customize the output. These functions were implemented as follows to present the output online:


function kalBlank() {

	document.write("<img align='TOP' src='blank.gif' alt=''>");

}



function kalBeforeToday() {

	document.write("<img align='TOP' src='blank.gif' alt='Before today'>");

}



function kalFree(year,month,day) {

	var wDate = day+". "+months[month]+" "+year;

	var popStr = "The "+wDate+" is vacant. Click here to contact TopHat.";

	document.write("<a target='f_tekst' href='../sendMail.jsp?msg=");

	document.write("I would like to be contacted regarding the  ");

	document.write(wDate+"&sendSMS=true' ");

	document.write("onMouseOver=\"window.status='"+popStr+"';return true;\" ");

	document.write("onMouseOut=\"window.status='';return true;\" ");

document.write("><img align='TOP' border='0' src='ledig.gif' ");

document.write("alt='"+popStr+"'></a>");

}





function kalBusy(year,month,day) {

	document.write("<img align='TOP' border='0' src='optaget.gif' ");

	document.write("alt='This date is occupied.'>");

}





Rendering the above into a document was done in the body as follows:




<body>

<script type="text/javascript" language="javascript">

	parseYear(array);

</script>

</body>





If you want the source, please contact me directly at sglasius@silverstream.com.
conclusion
As said, the above technique is very useful when a page contains repeated data elements where each element has alternate text, status-bar display hints and URLs that vary only by a few parameters. In the calendar example, this technique decreased the file size by almost 90%, thus reducing bandwidth and speeding page delivery.

This technique can be taken even further. For example, suppose you have a customer that uses an advertising agency to create their web pages and this agency does not have anyone capable of JSP or the like (or you don't want them to have direct database access). However, suppose the agency does have someone who is familiar with JavaScript. Because JavaScript files can be included in an HTML page using the <script> tag, it would be fairly easy to create a Java Server Page, which in turn can output some JavaScript containing one or more arrays. The agency's JavaScript programmer would then have access to the data from the database without connecting directly to the database.

Of course this approach is most useable on a small scale, but it is still a technique worth considering under the right circumstances. In fact, this technique is very similar to XML and XSL, but compressed even further, and it is optimal for rendering almost any kind of table information on web pages whenever low bandwidth and high maintainability is needed.