How to Create a Navigable Table with Ajax

  • google plus

In Brief...

Google Maps (http://maps.google.com) was one of the applications that brought so much attention to Ajax. One of the cool things about it is that it allows the user to drag maps around the screen seamlessly loading new sections. It does this by preloading the sections around the map that the user is likely to drag on to the screen. This same concept can be applied to other applications, such as navigable tables. Learn how to create a navigable table with Ajax in the following steps.

Take our Ajax Training course for free.

See the Course Outline and Register

Instructions

For these examples to work, more files are required than are available in this how to. To see these examples in action, take the Ajax training course.

  1. There are table-specific methods for adding rows and cells. The helper functions shown below makes use of these:
    function addRow(tableId, cells){
    	var tableElem = document.getElementById(tableId);
    	var newRow = tableElem.insertRow(tableElem.rows.length);
    	var newCell;
    	for (var i = 0; i < cells.length; i++) {
    		newCell = newRow.insertCell(newRow.cells.length);
    		newCell.innerHTML = cells[i];
    	}
    	return newRow;
    }
  2. Table elements have an insertRow() method, which takes one argument: the position at which to insert the row. The thead, tbody, and tfoot elements also have this method, so our addRow function above can take the id of any of the element types as the first argument. The method inserts an empty row which we then can fill with cells.
  3. Table rows have an insertCell() method. It also takes one argument: the position at which to insert the cell. The inserted cell is empty. Our addRow function above populates the cells from the elements in the passed-in cells array.
  4. We have also created a helper function for deleting rows:
    function deleteRow(tableId, rowNumber){
    	var tableElem = document.getElementById(tableId);
    	if (rowNumber >= 0 && rowNumber < tableElem.rows.length) {
    		tableElem.deleteRow(rowNumber);
    		return true;
    	} else {
    	return false; //no row to delete
    	}
    }
    It uses the deleteRow() method of table, thead, tbody, and tfoot elements to remove the row. If the passed-in rowNumber doesn't exist, the function fails silently by returning false.
  5. The following shows an example of this code in use:
    <!DOCTYPE HTML>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Table Rows</title>
    <link href="TableRows.css" type="text/css" rel="stylesheet">
    <script type="text/javascript" src="lib.js"></script>
    <script type="text/javascript">
    	var tableConfig = {
    		rowsToShow: 2,
    		totalRows: 10 //this number would normally be retrieved from the server
    	}
    
    	function prevRows() {
    		var curRow = Number(document.getElementById("CurRowStart").innerHTML);
    		getRows(curRow - tableConfig.rowsToShow, "prev");
    	}
    
    	function nextRows() {
    		var curRow = Number(document.getElementById("CurRowStart").innerHTML);
    		getRows(curRow + tableConfig.rowsToShow, "next");
    	}
    
    	function getRows(curRow, rowSource) {
    		var slideImage = document.getElementById("CurSlideImage");
    		var curRowStart = document.getElementById("CurRowStart");
    		var curRowEnd = document.getElementById("CurRowEnd");
    		var btnPrev = document.getElementById("PrevButton");
    		var btnNext = document.getElementById("NextButton");
    		var prevTbody = document.getElementById("PrevRows");
    		var nextTbody = document.getElementById("NextRows");
    		var curTbody = document.getElementById("CurRows");
    		var rowsToShow = tableConfig.rowsToShow;
    		var totalRows = tableConfig.totalRows;
    		var xmlhttp;
    
    		btnPrev.disabled = true;
    		btnNext.disabled = true;
    
    		if (rowSource == "prev") {
    			for (var i = curTbody.rows.length - 1; i >= 0; i--) {
    				curTbody.replaceChild(prevTbody.rows[i], curTbody.rows[i]);
    				deleteRow("NextRows", i);
    			}
    			curRowStart.innerHTML = curRow;
    			curRowEnd.innerHTML = curRow + rowsToShow - 1;
    			message("Loading from Prev");
    		} else if (rowSource == "next") {
    			for (var i = curTbody.rows.length - 1; i >= 0; i--) {
    				curTbody.replaceChild(nextTbody.rows[i], curTbody.rows[i]);
    				deleteRow("PrevRows", i);
    			}
    			curRowStart.innerHTML = curRow;
    			curRowEnd.innerHTML = curRow + rowsToShow - 1;
    			message("Loading from Next");
    		}
    		
    		url = "TableRows?Row=" + curRow + "&RowsToShow=" + rowsToShow;
    		xmlhttp = new XMLHttpRequest();
    		xmlhttp.open("get", url, true);
    
    		xmlhttp.onreadystatechange = function() {
    			if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
    				changeRows(xmlhttp);
    			}
    		}
    		xmlhttp.send(null);
    
    		function changeRows(xmlhttp) {
    			var docElem = xmlhttp.responseXML.documentElement;
    			var lastSetStart = totalRows - rowsToShow + 1;
    			var prevRowPos = (curRow == 1) ? null : 0;
    			var curRowPos = (curRow == 1) ? 0 : rowsToShow;
    			var nextRowPos = (curRow == 1) ? rowsToShow :
    				(curRow >= lastSetStart) ? null : 2 * rowsToShow;
    
    			removeWhitespace(docElem, true);
    
    			if (prevRowPos !== null) {
    				loadRows("Prev", prevRowPos, docElem);
    				btnPrev.disabled = false;
    			}
    
    			if (nextRowPos !== null) {
    				loadRows("Next", nextRowPos, docElem);
    				btnNext.disabled = false;
    			}
    
    			if (rowSource == "ajax") {
    				loadRows("Cur", curRowPos, docElem);
    				message("Loading from Ajax");
    			}
    
    			curRowStart.innerHTML = curRow;
    			curRowEnd.innerHTML = curRow + rowsToShow - 1;
    
    		}
    	}
    
    	function loadRows(rowset, startRow, docElem) {
    		var endRow = startRow + tableConfig.rowsToShow - 1;
    		var tbodyId = rowset + "Rows";
    		var cells = [],
    			row, cellText;
    		var rowLength = docElem.firstChild.childNodes.length;
    		var htmlRow;
    		for (var i = startRow; i <= endRow; i++) {
    			row = docElem.childNodes[i];
    			for (var j = 0; j < rowLength; j++) {
    				if (row) {
    					cellNode = row.childNodes[j];
    					cellText = cellNode.firstChild.nodeValue;
    					if (cellNode.nodeName == "Image") {
    						cellText = "<img src='Images/" + cellText + "'>";
    					}
    				} else {
    					cellText = "";
    				}
    				cells.push(cellText);
    			}
    			htmlRow = addRow(tbodyId, cells);
    			if (!row) htmlRow.style.display = "none";
    			cells = [];
    		}
    	}
    
    	function message(msg) {
    		var output = document.getElementById("Message");
    		output.innerHTML = msg;
    	}
    
    	observeEvent(window, "load", function() {
    		var btnPrev = document.getElementById("PrevButton");
    		var btnNext = document.getElementById("NextButton");
    		var totalRows = tableConfig.totalRows;
    		observeEvent(btnPrev, "click", prevRows);
    		observeEvent(btnNext, "click", nextRows);
    		document.getElementById("TotalRowsNum").innerHTML = totalRows;
    		getRows(1, "ajax");
    	});
    </script>
    </head>
    
    <body>
    <h1>First 10 Presidents</h1>
    <table id="Table">
    	<thead>
    		<tr>
    			<th>President</th>
    			<th>Years</th>
    			<th>Image</th>
    		</tr>
    	</thead>
    	<tbody id="PrevRows"></tbody>
    	<tbody id="CurRows"></tbody>
    	<tbody id="NextRows"></tbody>
    	<tfoot>
    		<tr>
    			<td colspan="3">
    				<button id="PrevButton">Previous</button>
    				Rows <span id="CurRowStart">1</span> - <span id="CurRowEnd"></span> of <span id="TotalRowsNum"></span>
    				<button id="NextButton">Next</button>
    			</td>
    		</tr>
    		<tr>
    			<td colspan="3" id="Message"></td>
    		</tr>
    	</tfoot>
    </table>
    </body>
    </html>
    This code renders the following:
    Table Rows
    Note that you must have the associated files to run with this code for it to display correctly.

Author: Chris Minnick

Chris is a prolific author and trainer, and the CEO of WatzThis?. His published books include Writing Computer Code, JavaScript for Kids, Coding with JavaScript For Dummies, Beginning HTML5 and CSS3 For Dummies, Webkit For Dummies, CIW eCommerce Certification Bible, and New Riders' XHTML.

Discuss