Navigate table cells like excel sheet

The attached code allows the user to create tables dynamically and add/delete rows/columns.

Is it possible to navigate within a table like the way we do in an excel sheet? When the focus is on a text box, and the user presses an arrow key, the focus should change to a different textbox according to the arrow key pressed.

Can anybody give me the code (as a function) that does the same?

Thanks!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>OOP Table Manipulation</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body, p, td, div { font-family: Calibri, Arial, Tahoma; }

table {
	margin:10px 0;
	border-collapse: collapse;
}

tr { margin-top:2px; }

td {
	margin-left:2px;
	padding: 2px;
}
div.newcol a {
	text-decoration: none;
}
.xmark {
	font-size:14px;
}
.xmark, .pmark {
	color:#888;
	text-decoration:none;
}
.pmark {
	font-size: 22px;
	margin-left: -8px;
}

.xTableMark {
	font-size:18px;
	color:#888;
	text-decoration:none;
}

.xmark:hover,
.xTableMark:hover { color:#F00; }

.tableContainer {
	display: inline-block;
	position: relative;
	padding: 0 20px 20px 0;
	overflow: visible;
}

.tableContainer img.button {
	position: absolute;
	z-index: 1000;
}

.tableContainer img.button:hover { cursor: pointer; }

.tableContainer img.newCol {
	top: 60px;
	right: 0px;
	width: 20px;
	height: 20px;
}

.tableContainer img.newRow {
	bottom: 5px;
	right: 100px;
	width: 16px;
	height: 16px;
}
</style>
<script type="text/javascript">
//<![CDATA[
var tableInstancePool = new Array();
var counter = 0;
var numTables = 0;

var dropDown = "<select name='' id='' style='width: 149px'><option value='1'>1</option><option value='2'>2</option><option value='3'>3</option></select>";
var textBox = "<input type='text'></input>";

var b = "1px solid red";

function highlightRow(cellElem) {

	// Cell that contains the delete link
	var cellElem = cellElem.parentNode;

	// Table that contains the cell
	var tbody = cellElem.parentNode.parentNode;

	// Get row index of current cell
	var rowIndex = -1;
	var rows = tbody.getElementsByTagName("tr");

	outerLoop:
	for(i=0; i<rows.length; i++) {

		var cells = rows[i].getElementsByTagName("td");
		for(j=0; j<cells.length; j++) {

			if(cells[j] == cellElem) {
				rowIndex = i;
				break outerLoop;
			}
		}
	}

	// Highlight all cells in same row
	if(rowIndex > 0) {
		var cells = rows[rowIndex].getElementsByTagName("td");

		cells[1].style.borderLeft = b;
		for(j=1; j<cells.length; j++) {

			cells[j].style.borderTop = b;
			cells[j].style.borderBottom = b;
		}
		cells[cells.length-1].style.borderRight = b;
	}
}

function highlightCol(cellElem) {

	// Cell that contains the delete link
	var cellElem = cellElem.parentNode;

	// Table that contains the cell
	var tbody = cellElem.parentNode.parentNode;

	// Get row index of current cell
	var colIndex = -1;
	var rows = tbody.getElementsByTagName("tr");

	outerLoop:
	for(i=0; i<rows.length; i++) {

		var cells = rows[i].getElementsByTagName("td");
		for(j=0; j<cells.length; j++) {

			if(cells[j] == cellElem) {
				colIndex = j;
				break outerLoop;
			}
		}
	}

	// Highlight all cells in same row
	if(colIndex > 0) {

		rows[1].getElementsByTagName("td")[colIndex].style.borderTop = b;
		for(i=1; i<rows.length; i++) {

			var cell = rows[i].getElementsByTagName("td")[colIndex];

			cell.style.borderLeft = b;
			cell.style.borderRight = b;
		}
		rows[rows.length-1].getElementsByTagName("td")[colIndex].style.borderBottom = b;
	}
}

function highlightTable(tableElem) {

	var tbody = tableElem.parentNode.parentNode.parentNode;

	var rows = tbody.getElementsByTagName("tr");
	for(i=1; i<rows.length; i++) {

		var cells = rows[i].getElementsByTagName("td");
		for(j=1; j<cells.length; j++) {

			if(i==1) cells[j].style.borderTop = b;
			if(j==1) cells[j].style.borderLeft = b;
			if(i==rows.length-1) cells[j].style.borderBottom = b;
			if(j==cells.length-1) cells[j].style.borderRight = b;
		}
	}
}

function clearHighlights(cellElem) {

	// Table that contains the cell
	var tbody = cellElem.parentNode.parentNode.parentNode;
	var cells = tbody.getElementsByTagName("td");
	for(i=0; i<cells.length; i++) {

		cells[i].style.borderLeft = '';
		cells[i].style.borderRight = '';
		cells[i].style.borderTop = '';
		cells[i].style.borderBottom = '';
	}
}

function table(numRows, numCols, parentId) {
	numTables++;
	name = ++counter;
	this.name = name;
	this.numRows = numRows;
	this.numCols = numCols;
	this.parentId = parentId;
	this.rowCounter = numRows;
	this.colCounter = numCols;

	var tableHTML = '<div class="tableContainer"><table id="id_' + name + '">';

	// first row with the table delete button
	tableHTML += '<td align="center" style="color:#F00;"><a href="javascript:void(0);" onmouseover="highlightTable(this);" onmouseout="clearHighlights(this);" class="xTableMark" title="Delete Table" onclick="tableInstancePool[\'' + this.name + '\'].deleteThisTable(this);">X</a><br /><input id="id_' + this.name + '_checkbox_checkboxno" type="checkbox" style="margin-left: 30px;" checked="checked" /></td>';

	// first row with the col delete buttons
	for (var colCounter = 0; colCounter < numCols; colCounter++) {
		tableHTML += '<td align="center" valign="bottom" style="color:#F00"><div style="float: left"><a class="pmark" href="#" style="text-decoration: none" onclick="tableInstancePool[\'' + this.name + '\'].addCol(this)">+</a></div><a href="#" class="xmark" onmouseover="highlightCol(this);" onmouseout="clearHighlights(this);" onclick="tableInstancePool[\'' + this.name + '\'].delCol(this.parentNode);">X</a></td>';
	}
	tableHTML += '</tr>';

	// second row with select boxes
	tableHTML += '<tr><td>&nbsp;</td>'; // no delete button on this row
	for (var colCounter = 0; colCounter < numCols; colCounter++) {
		tableHTML += '<td>' + dropDown + '</td>';
	}
	tableHTML += '</tr>';

	// remaining rows with input fields
	for (var rowCounter = 0; rowCounter < numRows; rowCounter++) {
		tableHTML += '<tr id="">';
		tableHTML += '<td align="right" style="color:#F00"><a href="#" class="xmark" onmouseover="highlightRow(this);" onmouseout="clearHighlights(this);" onclick="tableInstancePool[\'' + this.name + '\'].delRow(this.parentNode);">X</a></td>';
		for (var colCounter = 0; colCounter < numCols; colCounter++) {
			tableHTML += '<td id="">' + textBox + '</td>';
		}
		tableHTML += '</tr>';
	}
	tableHTML += '</table><img src="http://www.ict7.com/images/RightArrow2HS.png" class="newCol button" title="New Column" onclick="addColToTable(\''+ this.name +'\');" /><img src="http://confluence.atlassian.com/download/attachments/18579599/arrow_down_blue.gif" class="newRow button" title="New Row" onclick="addRowToTable(\''+ this.name +'\');" /></div>';

	var container = document.createElement("div");
	container.innerHTML = tableHTML;
	document.getElementById(parentId).appendChild(container);
	this.renameID();
}

table.prototype.addRow = addRow;
table.prototype.deleteRow = deleteRow;
table.prototype.deleteThisTable = deleteThisTable;
table.prototype.addCol = addCol;
table.prototype.delCol = delCol;
table.prototype.delRow = delRow;
table.prototype.renameID = renameID;

function addRow() {
	tableHTML = '<td align="right" style="color:#736F6E;"><a href="#" class="xmark" onmouseover="highlightRow(this);" onmouseout="clearHighlights(this);" onclick="tableInstancePool[\'' + this.name + '\'].delRow(this.parentNode);">X</a></td>';
	for (var colCounter = 0; colCounter < this.numCols; colCounter++) {
		tableHTML += "<td>" + textBox + "</td>";
	}

	this.rowCounter++;
	this.numRows++;
	var table = document.getElementById("id_" + this.name);
	table = table.getElementsByTagName("TBODY")[0];
	// add a new row to the table, and insert the cells we've just written
	rowElement = document.createElement('tr');
	newRow = table.appendChild(rowElement);
	newRow.innerHTML = tableHTML;
	this.renameID();
}

function addCol(row_link) {
	index = row_link.parentNode.parentNode.cellIndex-1;
	if (index == null) { index=this.numCols; }
	var table = document.getElementById("id_" + this.name);
	var rows = table.rows;
	var delCrossAdded = false;
	for (var rowCounter = 0; rowCounter < this.numRows + 2; rowCounter++) {
		var row = rows[rowCounter];
		var cell = row.insertCell(index + 1);
		if (delCrossAdded) {
			cell.id = "";
			cell.innerHTML = textBox;
		} else {
			delCrossAdded = true;
			cell.setAttribute("align", "center");
			cell.setAttribute("valign", "bottom");
			cell.setAttribute("style", "color:#F00");
			cell.innerHTML = '<div style="float: left"><a class="pmark" href="#" style="text-decoration: none" onclick="tableInstancePool[\'' + this.name + '\'].addCol(this)">+</a></div><a href="#" class="xmark" onmouseover="highlightCol(this);" onmouseout="clearHighlights(this);"  onclick="tableInstancePool[\'' + this.name + '\'].delCol(this.parentNode);">X</a>';

			// now add the second row with the select box
			rowCounter++;
			var row = rows[rowCounter];
			var cell = row.insertCell(index + 1);
			cell.innerHTML = dropDown;

		}
	}
	this.numCols++;
	this.colCounter++;
	this.renameID();
}

function delRow(rowObject) {
	//if(this.numRows <= 1) deleteTable(this.name);
	//else {
		rowObject.parentNode.parentNode.removeChild(rowObject.parentNode);
		this.numRows--;
		this.renameID();
	//}
}

function delCol(colObject) {
	if(this.numCols <= 1) deleteTable(this.name);
	else {
		var cells = colObject.parentNode.getElementsByTagName("td");
		var c = 0;
		for (c; c < cells.length; c++) if (cells[c] == colObject) break;
		var rows = colObject.parentNode.parentNode.getElementsByTagName("tr");
		for (var r = rows.length - 1; r >= 0; r--) colObject.parentNode.parentNode.rows[r].deleteCell(c);
		this.numCols--;
		this.renameID();
	}
}

function deleteRow(rowNumber) {
	this.numRows--;
	var row = document.getElementById("id_" + this.name + "tr_" + this.rowCounter);
	row.parentNode.remove(row);
}

function deleteThisTable() {
	numTables--;
	var table = document.getElementById("id_" + this.name);
	var div = table.parentNode.parentNode;
	div.parentNode.removeChild(div);
}

function createTable() {
	var rows = parseInt(document.getElementById("rows").value);
	var cols = parseInt(document.getElementById("cols").value);
	if (isNaN(rows)) rows = 2;
	if (isNaN(cols)) cols = 4;
	var newTable = new table(rows, cols, "tables");
	tableInstancePool[name] = newTable;
}

function deleteTable(tableName) {
	var tableInstance = tableInstancePool[tableName];
	tableInstance.deleteThisTable();
}

function addRowToTable(tableName) {
	var tableInstance = tableInstancePool[tableName];
	tableInstance.addRow();
}

function addColToTable(tableName, index) {
	var tableInstance = tableInstancePool[tableName];
	tableInstance.addCol(index);
}

function renameID() {
	var rows = document.getElementById("id_" + this.name).getElementsByTagName("tr");
	for (var i = 1; i < rows.length; i++) {
		rows[i].id = "id_" + this.name + "tr_" + (i - 1);

		cells = rows[i].getElementsByTagName("td");
		for (var j = 1; j < cells.length; j++) {
			cells[j].id = "id_" + this.name + "tr_" + (i - 2) + "td_" + (j - 1);
			selectBoxes = cells[j].getElementsByTagName("select");
			if (selectBoxes.length) {
				selectBoxes[0].id = "id_" + this.name + "dropdown_" + (j - 1);
				selectBoxes[0].name = "id_" + this.name + "dropdown_" + (j - 1);
			}
		}
	}
}
//]]>
</script>
</head>
    <body>
        Rows:
        <input id="rows" type="text" value="2" />
        <br />
        Cols:  
        <input id="cols" type="text" value="4" />
        <br />
        <a href="#" onclick="createTable(); return false;">Create Table</a>
        <div id="tables"></div>

    </body>
</html>

Open in new window

dshrenikAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

hpiersonCommented:
If you've tried this yourself, you know it's not trivial. Among other things, there are browser compatibility issues.

A lot of work has been done by a number of people to add this capability to jquery.

All the details on how this works and how to use it are here:

http://projects.arkanis-development.de/table_navigation/

I don't want to take credit for other people's work, but it answers your question.
dshrenikAuthor Commented:
Thanks for the link!
If possible, please modify my code by integrating the two.

Thanks!
hpiersonCommented:
OK, here's something instead that IS my own work, integrated into your code.

Basically, I added an onkeypress event into the cells, and changed the cell ids to make them a little easier to parse (ie I added a '*' between the parts)

Tested, it works. Points Please!

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>OOP Table Manipulation</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body, p, td, div { font-family: Calibri, Arial, Tahoma; }

table {
	margin:10px 0;
	border-collapse: collapse;
}

tr { margin-top:2px; }

td {
	margin-left:2px;
	padding: 2px;
}
div.newcol a {
	text-decoration: none;
}
.xmark {
	font-size:14px;
}
.xmark, .pmark {
	color:#888;
	text-decoration:none;
}
.pmark {
	font-size: 22px;
	margin-left: -8px;
}

.xTableMark {
	font-size:18px;
	color:#888;
	text-decoration:none;
}

.xmark:hover,
.xTableMark:hover { color:#F00; }

.tableContainer {
	display: inline-block;
	position: relative;
	padding: 0 20px 20px 0;
	overflow: visible;
}

.tableContainer img.button {
	position: absolute;
	z-index: 1000;
}

.tableContainer img.button:hover { cursor: pointer; }

.tableContainer img.newCol {
	top: 60px;
	right: 0px;
	width: 20px;
	height: 20px;
}

.tableContainer img.newRow {
	bottom: 5px;
	right: 100px;
	width: 16px;
	height: 16px;
}
</style>
<script type="text/javascript">
//<![CDATA[
var tableInstancePool = new Array();
var counter = 0;
var numTables = 0;

var dropDown = "<select name='' id='' style='width: 149px'><option value='1'>1</option><option value='2'>2</option><option value='3'>3</option></select>";
var textBox = "<input type='text'></input>";

var b = "1px solid red";

function move(el) {
	var did = el.id;
//	alert(did);
	var parts = did.split('*');
	var grow = parts[1].split('_');
	var gcol = parts[2].split('_');
	var currow = parseInt(grow[1]);
	var curcol = parseInt(gcol[1]);
if (window.event) {
var key = window.event.keyCode;
window.status = key;
var newcol = curcol;
var newrow = currow;
if (key == 37) { // left arrow
if (curcol > 0) {var newcol = curcol - 1;}
} else if (key == 38) { // up arrow
if (currow > 0 ) {var newrow = currow - 1;}
} else if (key == 39) { // right arrow
newcol = curcol + 1;
} else if (key == 40) { // down arrow
newrow = currow + 1;
}
}	
var newid = parts[0] + '*tr_' + newrow + '*td_' + newcol;	
var element = document.getElementById(newid);
var inp = element.getElementsByTagName('input');
inp[0].focus();
}

function highlightRow(cellElem) {

	// Cell that contains the delete link
	var cellElem = cellElem.parentNode;

	// Table that contains the cell
	var tbody = cellElem.parentNode.parentNode;

	// Get row index of current cell
	var rowIndex = -1;
	var rows = tbody.getElementsByTagName("tr");

	outerLoop:
	for(i=0; i<rows.length; i++) {

		var cells = rows[i].getElementsByTagName("td");
		for(j=0; j<cells.length; j++) {

			if(cells[j] == cellElem) {
				rowIndex = i;
				break outerLoop;
			}
		}
	}

	// Highlight all cells in same row
	if(rowIndex > 0) {
		var cells = rows[rowIndex].getElementsByTagName("td");

		cells[1].style.borderLeft = b;
		for(j=1; j<cells.length; j++) {

			cells[j].style.borderTop = b;
			cells[j].style.borderBottom = b;
		}
		cells[cells.length-1].style.borderRight = b;
	}
}

function highlightCol(cellElem) {

	// Cell that contains the delete link
	var cellElem = cellElem.parentNode;

	// Table that contains the cell
	var tbody = cellElem.parentNode.parentNode;

	// Get row index of current cell
	var colIndex = -1;
	var rows = tbody.getElementsByTagName("tr");

	outerLoop:
	for(i=0; i<rows.length; i++) {

		var cells = rows[i].getElementsByTagName("td");
		for(j=0; j<cells.length; j++) {

			if(cells[j] == cellElem) {
				colIndex = j;
				break outerLoop;
			}
		}
	}

	// Highlight all cells in same row
	if(colIndex > 0) {

		rows[1].getElementsByTagName("td")[colIndex].style.borderTop = b;
		for(i=1; i<rows.length; i++) {

			var cell = rows[i].getElementsByTagName("td")[colIndex];

			cell.style.borderLeft = b;
			cell.style.borderRight = b;
		}
		rows[rows.length-1].getElementsByTagName("td")[colIndex].style.borderBottom = b;
	}
}

function highlightTable(tableElem) {

	var tbody = tableElem.parentNode.parentNode.parentNode;

	var rows = tbody.getElementsByTagName("tr");
	for(i=1; i<rows.length; i++) {

		var cells = rows[i].getElementsByTagName("td");
		for(j=1; j<cells.length; j++) {

			if(i==1) cells[j].style.borderTop = b;
			if(j==1) cells[j].style.borderLeft = b;
			if(i==rows.length-1) cells[j].style.borderBottom = b;
			if(j==cells.length-1) cells[j].style.borderRight = b;
		}
	}
}

function clearHighlights(cellElem) {

	// Table that contains the cell
	var tbody = cellElem.parentNode.parentNode.parentNode;
	var cells = tbody.getElementsByTagName("td");
	for(i=0; i<cells.length; i++) {

		cells[i].style.borderLeft = '';
		cells[i].style.borderRight = '';
		cells[i].style.borderTop = '';
		cells[i].style.borderBottom = '';
	}
}

function table(numRows, numCols, parentId) {
	numTables++;
	name = ++counter;
	this.name = name;
	this.numRows = numRows;
	this.numCols = numCols;
	this.parentId = parentId;
	this.rowCounter = numRows;
	this.colCounter = numCols;

	var tableHTML = '<div class="tableContainer"><table id="id_' + name + '">';

	// first row with the table delete button
	tableHTML += '<td align="center" style="color:#F00;"><a href="javascript:void(0);" onmouseover="highlightTable(this);" onmouseout="clearHighlights(this);" class="xTableMark" title="Delete Table" onclick="tableInstancePool[\'' + this.name + '\'].deleteThisTable(this);">X</a><br /><input id="id_' + this.name + '_checkbox_checkboxno" type="checkbox" style="margin-left: 30px;" checked="checked" /></td>';

	// first row with the col delete buttons
	for (var colCounter = 0; colCounter < numCols; colCounter++) {
		tableHTML += '<td align="center" valign="bottom" style="color:#F00"><div style="float: left"><a class="pmark" href="#" style="text-decoration: none" onclick="tableInstancePool[\'' + this.name + '\'].addCol(this)">+</a></div><a href="#" class="xmark" onmouseover="highlightCol(this);" onmouseout="clearHighlights(this);" onclick="tableInstancePool[\'' + this.name + '\'].delCol(this.parentNode);">X</a></td>';
	}
	tableHTML += '</tr>';

	// second row with select boxes
	tableHTML += '<tr><td>&nbsp;</td>'; // no delete button on this row
	for (var colCounter = 0; colCounter < numCols; colCounter++) {
		tableHTML += '<td>' + dropDown + '</td>';
	}
	tableHTML += '</tr>';

	// remaining rows with input fields
	for (var rowCounter = 0; rowCounter < numRows; rowCounter++) {
		tableHTML += '<tr id="">';
		tableHTML += '<td align="right" style="color:#F00"><a href="#" class="xmark" onmouseover="highlightRow(this);" onmouseout="clearHighlights(this);" onclick="tableInstancePool[\'' + this.name + '\'].delRow(this.parentNode);">X</a></td>';
		for (var colCounter = 0; colCounter < numCols; colCounter++) {
			tableHTML += '<td id="" onkeyup="move(this);">' + textBox + '</td>';
		}
		tableHTML += '</tr>';
	}
	tableHTML += '</table><img src="http://www.ict7.com/images/RightArrow2HS.png" class="newCol button" title="New Column" onclick="addColToTable(\''+ this.name +'\');" /><img src="http://confluence.atlassian.com/download/attachments/18579599/arrow_down_blue.gif" class="newRow button" title="New Row" onclick="addRowToTable(\''+ this.name +'\');" /></div>';

	var container = document.createElement("div");
	container.innerHTML = tableHTML;
	document.getElementById(parentId).appendChild(container);
	this.renameID();
}

table.prototype.addRow = addRow;
table.prototype.deleteRow = deleteRow;
table.prototype.deleteThisTable = deleteThisTable;
table.prototype.addCol = addCol;
table.prototype.delCol = delCol;
table.prototype.delRow = delRow;
table.prototype.renameID = renameID;

function addRow() {
	tableHTML = '<td align="right" style="color:#736F6E;"><a href="#" class="xmark" onmouseover="highlightRow(this);" onmouseout="clearHighlights(this);" onclick="tableInstancePool[\'' + this.name + '\'].delRow(this.parentNode);">X</a></td>';
	for (var colCounter = 0; colCounter < this.numCols; colCounter++) {
		tableHTML += "<td onkeyup='move(this);'>" + textBox + "</td>";
	}

	this.rowCounter++;
	this.numRows++;
	var table = document.getElementById("id_" + this.name);
	table = table.getElementsByTagName("TBODY")[0];
	// add a new row to the table, and insert the cells we've just written
	rowElement = document.createElement('tr');
	newRow = table.appendChild(rowElement);
	newRow.innerHTML = tableHTML;
	this.renameID();
}

function addCol(row_link) {
	index = row_link.parentNode.parentNode.cellIndex-1;
	if (index == null) { index=this.numCols; }
	var table = document.getElementById("id_" + this.name);
	var rows = table.rows;
	var delCrossAdded = false;
	for (var rowCounter = 0; rowCounter < this.numRows + 2; rowCounter++) {
		var row = rows[rowCounter];
		var cell = row.insertCell(index + 1);
		if (delCrossAdded) {
			cell.id = "";
			cell.innerHTML = textBox;
		} else {
			delCrossAdded = true;
			cell.setAttribute("align", "center");
			cell.setAttribute("valign", "bottom");
			cell.setAttribute("style", "color:#F00");
			cell.innerHTML = '<div style="float: left"><a class="pmark" href="#" style="text-decoration: none" onclick="tableInstancePool[\'' + this.name + '\'].addCol(this)">+</a></div><a href="#" class="xmark" onmouseover="highlightCol(this);" onmouseout="clearHighlights(this);"  onclick="tableInstancePool[\'' + this.name + '\'].delCol(this.parentNode);">X</a>';

			// now add the second row with the select box
			rowCounter++;
			var row = rows[rowCounter];
			var cell = row.insertCell(index + 1);
			cell.innerHTML = dropDown;

		}
	}
	this.numCols++;
	this.colCounter++;
	this.renameID();
}

function delRow(rowObject) {
	//if(this.numRows <= 1) deleteTable(this.name);
	//else {
		rowObject.parentNode.parentNode.removeChild(rowObject.parentNode);
		this.numRows--;
		this.renameID();
	//}
}

function delCol(colObject) {
	if(this.numCols <= 1) deleteTable(this.name);
	else {
		var cells = colObject.parentNode.getElementsByTagName("td");
		var c = 0;
		for (c; c < cells.length; c++) if (cells[c] == colObject) break;
		var rows = colObject.parentNode.parentNode.getElementsByTagName("tr");
		for (var r = rows.length - 1; r >= 0; r--) colObject.parentNode.parentNode.rows[r].deleteCell(c);
		this.numCols--;
		this.renameID();
	}
}

function deleteRow(rowNumber) {
	this.numRows--;
	var row = document.getElementById("id_" + this.name + "*tr_" + this.rowCounter);
	row.parentNode.remove(row);
}

function deleteThisTable() {
	numTables--;
	var table = document.getElementById("id_" + this.name);
	var div = table.parentNode.parentNode;
	div.parentNode.removeChild(div);
}

function createTable() {
	var rows = parseInt(document.getElementById("rows").value);
	var cols = parseInt(document.getElementById("cols").value);
	if (isNaN(rows)) rows = 2;
	if (isNaN(cols)) cols = 4;
	var newTable = new table(rows, cols, "tables");
	tableInstancePool[name] = newTable;
}

function deleteTable(tableName) {
	var tableInstance = tableInstancePool[tableName];
	tableInstance.deleteThisTable();
}

function addRowToTable(tableName) {
	var tableInstance = tableInstancePool[tableName];
	tableInstance.addRow();
}

function addColToTable(tableName, index) {
	var tableInstance = tableInstancePool[tableName];
	tableInstance.addCol(index);
}

function renameID() {
	var rows = document.getElementById("id_" + this.name).getElementsByTagName("tr");
	for (var i = 1; i < rows.length; i++) {
		rows[i].id = "id_" + this.name + "*tr_" + (i - 1);

		cells = rows[i].getElementsByTagName("td");
		for (var j = 1; j < cells.length; j++) {
			cells[j].id = "id_" + this.name + "*tr_" + (i - 2) + "*td_" + (j - 1);
			selectBoxes = cells[j].getElementsByTagName("select");
			if (selectBoxes.length) {
				selectBoxes[0].id = "id_" + this.name + "dropdown_" + (j - 1);
				selectBoxes[0].name = "id_" + this.name + "dropdown_" + (j - 1);
			}
		}
	}
}
//]]>
</script>
</head>
    <body>
        Rows:
        <input id="rows" type="text" value="2" />
        <br />
        Cols:  
        <input id="cols" type="text" value="4" />
        <br />
        <a href="#" onclick="createTable(); return false;">Create Table</a>
        <div id="tables"></div>

    </body>
</html>

Open in new window

Your Guide to Achieving IT Business Success

The IT Service Excellence Tool Kit has best practices to keep your clients happy and business booming. Inside, you’ll find everything you need to increase client satisfaction and retention, become more competitive, and increase your overall success.

hpiersonCommented:

please note also, if you have multiple tables, the navigation stays within each table (ie the up and down arrow keys will not take you beyond the boundary of the table you are in.)
hpiersonCommented:
Dang, sigh, that worked with IE and Chrome, but FF was recalcitrant.

So here's a slightly altered version that works with all three browsers:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>OOP Table Manipulation</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body, p, td, div { font-family: Calibri, Arial, Tahoma; }

table {
	margin:10px 0;
	border-collapse: collapse;
}

tr { margin-top:2px; }

td {
	margin-left:2px;
	padding: 2px;
}
div.newcol a {
	text-decoration: none;
}
.xmark {
	font-size:14px;
}
.xmark, .pmark {
	color:#888;
	text-decoration:none;
}
.pmark {
	font-size: 22px;
	margin-left: -8px;
}

.xTableMark {
	font-size:18px;
	color:#888;
	text-decoration:none;
}

.xmark:hover,
.xTableMark:hover { color:#F00; }

.tableContainer {
	display: inline-block;
	position: relative;
	padding: 0 20px 20px 0;
	overflow: visible;
}

.tableContainer img.button {
	position: absolute;
	z-index: 1000;
}

.tableContainer img.button:hover { cursor: pointer; }

.tableContainer img.newCol {
	top: 60px;
	right: 0px;
	width: 20px;
	height: 20px;
}

.tableContainer img.newRow {
	bottom: 5px;
	right: 100px;
	width: 16px;
	height: 16px;
}
</style>
<script type="text/javascript">
//<![CDATA[
var tableInstancePool = new Array();
var counter = 0;
var numTables = 0;

var dropDown = "<select name='' id='' style='width: 149px'><option value='1'>1</option><option value='2'>2</option><option value='3'>3</option></select>";
var textBox = "<input type='text'></input>";

var b = "1px solid red";

function move(e, el) {
	var did = el.id;
//	alert(did);
	var parts = did.split('*');
	var grow = parts[1].split('_');
	var gcol = parts[2].split('_');
	var currow = parseInt(grow[1]);
	var curcol = parseInt(gcol[1]);

var key = (window.event) ? event.keyCode : e.keyCode;

window.status = key;
var newcol = curcol;
var newrow = currow;
if (key == 37) { // left arrow
if (curcol > 0) {var newcol = curcol - 1;}
} else if (key == 38) { // up arrow
if (currow > 0 ) {var newrow = currow - 1;}
} else if (key == 39) { // right arrow
newcol = curcol + 1;
} else if (key == 40) { // down arrow
newrow = currow + 1;
}

var newid = parts[0] + '*tr_' + newrow + '*td_' + newcol;	
var element = document.getElementById(newid);
var inp = element.getElementsByTagName('input');
inp[0].focus();
}

function highlightRow(cellElem) {

	// Cell that contains the delete link
	var cellElem = cellElem.parentNode;

	// Table that contains the cell
	var tbody = cellElem.parentNode.parentNode;

	// Get row index of current cell
	var rowIndex = -1;
	var rows = tbody.getElementsByTagName("tr");

	outerLoop:
	for(i=0; i<rows.length; i++) {

		var cells = rows[i].getElementsByTagName("td");
		for(j=0; j<cells.length; j++) {

			if(cells[j] == cellElem) {
				rowIndex = i;
				break outerLoop;
			}
		}
	}

	// Highlight all cells in same row
	if(rowIndex > 0) {
		var cells = rows[rowIndex].getElementsByTagName("td");

		cells[1].style.borderLeft = b;
		for(j=1; j<cells.length; j++) {

			cells[j].style.borderTop = b;
			cells[j].style.borderBottom = b;
		}
		cells[cells.length-1].style.borderRight = b;
	}
}

function highlightCol(cellElem) {

	// Cell that contains the delete link
	var cellElem = cellElem.parentNode;

	// Table that contains the cell
	var tbody = cellElem.parentNode.parentNode;

	// Get row index of current cell
	var colIndex = -1;
	var rows = tbody.getElementsByTagName("tr");

	outerLoop:
	for(i=0; i<rows.length; i++) {

		var cells = rows[i].getElementsByTagName("td");
		for(j=0; j<cells.length; j++) {

			if(cells[j] == cellElem) {
				colIndex = j;
				break outerLoop;
			}
		}
	}

	// Highlight all cells in same row
	if(colIndex > 0) {

		rows[1].getElementsByTagName("td")[colIndex].style.borderTop = b;
		for(i=1; i<rows.length; i++) {

			var cell = rows[i].getElementsByTagName("td")[colIndex];

			cell.style.borderLeft = b;
			cell.style.borderRight = b;
		}
		rows[rows.length-1].getElementsByTagName("td")[colIndex].style.borderBottom = b;
	}
}

function highlightTable(tableElem) {

	var tbody = tableElem.parentNode.parentNode.parentNode;

	var rows = tbody.getElementsByTagName("tr");
	for(i=1; i<rows.length; i++) {

		var cells = rows[i].getElementsByTagName("td");
		for(j=1; j<cells.length; j++) {

			if(i==1) cells[j].style.borderTop = b;
			if(j==1) cells[j].style.borderLeft = b;
			if(i==rows.length-1) cells[j].style.borderBottom = b;
			if(j==cells.length-1) cells[j].style.borderRight = b;
		}
	}
}

function clearHighlights(cellElem) {

	// Table that contains the cell
	var tbody = cellElem.parentNode.parentNode.parentNode;
	var cells = tbody.getElementsByTagName("td");
	for(i=0; i<cells.length; i++) {

		cells[i].style.borderLeft = '';
		cells[i].style.borderRight = '';
		cells[i].style.borderTop = '';
		cells[i].style.borderBottom = '';
	}
}

function table(numRows, numCols, parentId) {
	numTables++;
	name = ++counter;
	this.name = name;
	this.numRows = numRows;
	this.numCols = numCols;
	this.parentId = parentId;
	this.rowCounter = numRows;
	this.colCounter = numCols;

	var tableHTML = '<div class="tableContainer"><table id="id_' + name + '">';

	// first row with the table delete button
	tableHTML += '<td align="center" style="color:#F00;"><a href="javascript:void(0);" onmouseover="highlightTable(this);" onmouseout="clearHighlights(this);" class="xTableMark" title="Delete Table" onclick="tableInstancePool[\'' + this.name + '\'].deleteThisTable(this);">X</a><br /><input id="id_' + this.name + '_checkbox_checkboxno" type="checkbox" style="margin-left: 30px;" checked="checked" /></td>';

	// first row with the col delete buttons
	for (var colCounter = 0; colCounter < numCols; colCounter++) {
		tableHTML += '<td align="center" valign="bottom" style="color:#F00"><div style="float: left"><a class="pmark" href="#" style="text-decoration: none" onclick="tableInstancePool[\'' + this.name + '\'].addCol(this)">+</a></div><a href="#" class="xmark" onmouseover="highlightCol(this);" onmouseout="clearHighlights(this);" onclick="tableInstancePool[\'' + this.name + '\'].delCol(this.parentNode);">X</a></td>';
	}
	tableHTML += '</tr>';

	// second row with select boxes
	tableHTML += '<tr><td>&nbsp;</td>'; // no delete button on this row
	for (var colCounter = 0; colCounter < numCols; colCounter++) {
		tableHTML += '<td>' + dropDown + '</td>';
	}
	tableHTML += '</tr>';

	// remaining rows with input fields
	for (var rowCounter = 0; rowCounter < numRows; rowCounter++) {
		tableHTML += '<tr id="">';
		tableHTML += '<td align="right" style="color:#F00"><a href="#" class="xmark" onmouseover="highlightRow(this);" onmouseout="clearHighlights(this);" onclick="tableInstancePool[\'' + this.name + '\'].delRow(this.parentNode);">X</a></td>';
		for (var colCounter = 0; colCounter < numCols; colCounter++) {
			tableHTML += '<td id="" onkeyup="move(event,this);">' + textBox + '</td>';
		}
		tableHTML += '</tr>';
	}
	tableHTML += '</table><img src="http://www.ict7.com/images/RightArrow2HS.png" class="newCol button" title="New Column" onclick="addColToTable(\''+ this.name +'\');" /><img src="http://confluence.atlassian.com/download/attachments/18579599/arrow_down_blue.gif" class="newRow button" title="New Row" onclick="addRowToTable(\''+ this.name +'\');" /></div>';

	var container = document.createElement("div");
	container.innerHTML = tableHTML;
	document.getElementById(parentId).appendChild(container);
	this.renameID();
}

table.prototype.addRow = addRow;
table.prototype.deleteRow = deleteRow;
table.prototype.deleteThisTable = deleteThisTable;
table.prototype.addCol = addCol;
table.prototype.delCol = delCol;
table.prototype.delRow = delRow;
table.prototype.renameID = renameID;

function addRow() {
	tableHTML = '<td align="right" style="color:#736F6E;"><a href="#" class="xmark" onmouseover="highlightRow(this);" onmouseout="clearHighlights(this);" onclick="tableInstancePool[\'' + this.name + '\'].delRow(this.parentNode);">X</a></td>';
	for (var colCounter = 0; colCounter < this.numCols; colCounter++) {
		tableHTML += "<td onkeyup='move(event,this);'>" + textBox + "</td>";
	}

	this.rowCounter++;
	this.numRows++;
	var table = document.getElementById("id_" + this.name);
	table = table.getElementsByTagName("TBODY")[0];
	// add a new row to the table, and insert the cells we've just written
	rowElement = document.createElement('tr');
	newRow = table.appendChild(rowElement);
	newRow.innerHTML = tableHTML;
	this.renameID();
}

function addCol(row_link) {
	index = row_link.parentNode.parentNode.cellIndex-1;
	if (index == null) { index=this.numCols; }
	var table = document.getElementById("id_" + this.name);
	var rows = table.rows;
	var delCrossAdded = false;
	for (var rowCounter = 0; rowCounter < this.numRows + 2; rowCounter++) {
		var row = rows[rowCounter];
		var cell = row.insertCell(index + 1);
		if (delCrossAdded) {
			cell.id = "";
			cell.innerHTML = textBox;
		} else {
			delCrossAdded = true;
			cell.setAttribute("align", "center");
			cell.setAttribute("valign", "bottom");
			cell.setAttribute("style", "color:#F00");
			cell.innerHTML = '<div style="float: left"><a class="pmark" href="#" style="text-decoration: none" onclick="tableInstancePool[\'' + this.name + '\'].addCol(this)">+</a></div><a href="#" class="xmark" onmouseover="highlightCol(this);" onmouseout="clearHighlights(this);"  onclick="tableInstancePool[\'' + this.name + '\'].delCol(this.parentNode);">X</a>';

			// now add the second row with the select box
			rowCounter++;
			var row = rows[rowCounter];
			var cell = row.insertCell(index + 1);
			cell.innerHTML = dropDown;

		}
	}
	this.numCols++;
	this.colCounter++;
	this.renameID();
}

function delRow(rowObject) {
	//if(this.numRows <= 1) deleteTable(this.name);
	//else {
		rowObject.parentNode.parentNode.removeChild(rowObject.parentNode);
		this.numRows--;
		this.renameID();
	//}
}

function delCol(colObject) {
	if(this.numCols <= 1) deleteTable(this.name);
	else {
		var cells = colObject.parentNode.getElementsByTagName("td");
		var c = 0;
		for (c; c < cells.length; c++) if (cells[c] == colObject) break;
		var rows = colObject.parentNode.parentNode.getElementsByTagName("tr");
		for (var r = rows.length - 1; r >= 0; r--) colObject.parentNode.parentNode.rows[r].deleteCell(c);
		this.numCols--;
		this.renameID();
	}
}

function deleteRow(rowNumber) {
	this.numRows--;
	var row = document.getElementById("id_" + this.name + "*tr_" + this.rowCounter);
	row.parentNode.remove(row);
}

function deleteThisTable() {
	numTables--;
	var table = document.getElementById("id_" + this.name);
	var div = table.parentNode.parentNode;
	div.parentNode.removeChild(div);
}

function createTable() {
	var rows = parseInt(document.getElementById("rows").value);
	var cols = parseInt(document.getElementById("cols").value);
	if (isNaN(rows)) rows = 2;
	if (isNaN(cols)) cols = 4;
	var newTable = new table(rows, cols, "tables");
	tableInstancePool[name] = newTable;
}

function deleteTable(tableName) {
	var tableInstance = tableInstancePool[tableName];
	tableInstance.deleteThisTable();
}

function addRowToTable(tableName) {
	var tableInstance = tableInstancePool[tableName];
	tableInstance.addRow();
}

function addColToTable(tableName, index) {
	var tableInstance = tableInstancePool[tableName];
	tableInstance.addCol(index);
}

function renameID() {
	var rows = document.getElementById("id_" + this.name).getElementsByTagName("tr");
	for (var i = 1; i < rows.length; i++) {
		rows[i].id = "id_" + this.name + "*tr_" + (i - 1);

		cells = rows[i].getElementsByTagName("td");
		for (var j = 1; j < cells.length; j++) {
			cells[j].id = "id_" + this.name + "*tr_" + (i - 2) + "*td_" + (j - 1);
			selectBoxes = cells[j].getElementsByTagName("select");
			if (selectBoxes.length) {
				selectBoxes[0].id = "id_" + this.name + "dropdown_" + (j - 1);
				selectBoxes[0].name = "id_" + this.name + "dropdown_" + (j - 1);
			}
		}
	}
}
//]]>
</script>
</head>
    <body>
        Rows:
        <input id="rows" type="text" value="2" />
        <br />
        Cols:  
        <input id="cols" type="text" value="4" />
        <br />
        <a href="#" onclick="createTable(); return false;">Create Table</a>
        <div id="tables"></div>

    </body>
</html>

Open in new window

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
dshrenikAuthor Commented:
Just 1 small bug (if I can call it a bug!)
Please answer it here:
http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_26703714.html

Thank you so much!
dshrenikAuthor Commented:
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
JavaScript

From novice to tech pro — start learning today.