Link to home
Create AccountLog in
Avatar of dshrenik
dshrenikFlag for United States of America

asked on

Bug in code to create table with excel like navigation

The attached code allows creation of tables and navigation with the arrow keys.
The only bug is that when a textbox with text is present and I press the left or right arrow key, it shifts the cursor position first, and then changes focus to a different textbox.

It will be nice if the focus changes without changing the cursor position in the current textbox.

If possible, please fix this bug.

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 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

Avatar of dshrenik
dshrenik
Flag of United States of America image

ASKER

There's another bug:
When I create a new column, the navigation doesn't work from cells of that column.
Also, can we a grey border around the text box that currently holds the cursor?
ASKER CERTIFIED SOLUTION
Avatar of hpierson
hpierson

Link to home
membership
Create an account to see this answer
Signing up is free. No credit card required.
Create Account
Thank you!!!