我们可以为HTML表格增加编辑功能,具体表现为:当用户单击某个单元格(术语:激活),该单元格位置上出现编辑框,背景也与其它未激活的单元格不同,以示区别,当用户完成编辑后,单元格内容被更新。如果用户使用方向键,则变化单元格的激活状态。整个情况类似于Excel中的操作。
要实现上述目的,我们首先要处理单元格的在激活事件(onactivate)和去激活事件(ondeactivate),具体实现如下:
// 单元格的在激活事件
function onCellActivate ()
{
var parentTbl = this.parentElement.parentElement; // 单元格的父容器,在HTML元素中,单元格是<TD>,其父就是<TR>,<TR>的父就是<TABLE>
// 清除前一个被激活单元格状态
if ( parentTbl.activeCell != null ) // 前一个在激活的单元格存在?
{
if ( parentTbl.activeCell != this ) // 与当前相同是不是同一个?
{
parentTbl.activeCell.style.backgroundColor = ''; // 不是同一个,则清除背景
parentTbl.activeCell.innerHTML = parentTbl.activeCell.dataField.length > 0 ? parentTbl.activeCell.value2 : ' '; // 恢复显示内容
}
}
if ( parentTbl.activeCell != this ) // 如果当前单元格和前一个被激活的单元格不是同一个
{
parentTbl.activeCell = this; // 设置当前单元格为被激活的单元格
// 插入编辑框
this.innerHTML = '<INPUT type="text" value="' + this.dataField +
'" style="border-top:none;border-left:none;border-right:none;border-bottom:1px dashed black;background-color:transparent;font-weight: bold; color: black;" ' +
' onkeydown="return onTextBoxKeydown();" ' +
' onchange="return onTextBoxChange();" ></INPUT>';
var textBox = this.children[0]; // 取得对象
textBox.width = this.offsetWidth; // 设置宽度
textBox.align = 'right'; // 对齐方式,不起作用
textBox.setActive();
textBox.focus();
this.style.backgroundColor = '#CCCCFF'; // 设置显眼的背景,表示处于激活状态
}
}
// 单元格的去激活事件
// 当单元格被激活,编辑框出现,单元格就会失去激活,此事件就会激发
function onCellDeactivate ()
{
var parentTbl = this.parentElement.parentElement; // 获取Table
if ( parentTbl.activeCell != this ) // 如果被激活单元格不是当前单元格?
{
this.innerHTML = this.dataField.length > 0 ? this.value2 : ' '; // 恢复显示内容
}
}
// 单元格的在激活事件
function onCellActivate ()
{
var parentTbl = this.parentElement.parentElement; // 单元格的父容器,在HTML元素中,单元格是<TD>,其父就是<TR>,<TR>的父就是<TABLE>
// 清除前一个被激活单元格状态
if ( parentTbl.activeCell != null ) // 前一个在激活的单元格存在?
{
if ( parentTbl.activeCell != this ) // 与当前相同是不是同一个?
{
parentTbl.activeCell.style.backgroundColor = ''; // 不是同一个,则清除背景
parentTbl.activeCell.innerHTML = parentTbl.activeCell.dataField.length > 0 ? parentTbl.activeCell.value2 : ' '; // 恢复显示内容
}
}
if ( parentTbl.activeCell != this ) // 如果当前单元格和前一个被激活的单元格不是同一个
{
parentTbl.activeCell = this; // 设置当前单元格为被激活的单元格
// 插入编辑框
this.innerHTML = '<INPUT type="text" value="' + this.dataField +
'" style="border-top:none;border-left:none;border-right:none;border-bottom:1px dashed black;background-color:transparent;font-weight: bold; color: black;" ' +
' onkeydown="return onTextBoxKeydown();" ' +
' onchange="return onTextBoxChange();" ></INPUT>';
var textBox = this.children[0]; // 取得对象
textBox.width = this.offsetWidth; // 设置宽度
textBox.align = 'right'; // 对齐方式,不起作用
textBox.setActive();
textBox.focus();
this.style.backgroundColor = '#CCCCFF'; // 设置显眼的背景,表示处于激活状态
}
}
// 单元格的去激活事件
// 当单元格被激活,编辑框出现,单元格就会失去激活,此事件就会激发
function onCellDeactivate ()
{
var parentTbl = this.parentElement.parentElement; // 获取Table
if ( parentTbl.activeCell != this ) // 如果被激活单元格不是当前单元格?
{
this.innerHTML = this.dataField.length > 0 ? this.value2 : ' '; // 恢复显示内容
}
}当然,HTML不像窗口
程序,可以在设计时就挂接事件,因此,我们需要在页面加载完成后就为除行列标头之外的单元格挂接事件,进行初始化,同时可以进行还有跨行的单元格合并:
view plaincopy to clipboardprint?
document.onreadystatechange = function() // 在页面加载完成后,初始化TABLE
{
if ( this.readyState == 'complete' )
{
initTable(); // 初始化
}
}
function initTable()
{
for(var idx in document.worksheet ) // 遍历所有表
{
var tbl = $get(document.worksheet[idx].table); // 获取TABLE对象
for( var i = tbl.cells.length - 1; i > 1; i-- ) // 从后往前遍历所有单元格
{
var ctrl = tbl.cells[i]; // 单元格对象
if ( ctrl.cellIndex > 0 && ctrl.parentElement.rowIndex > 0) // 第一行和第一列不处理
{
ctrl.onactivate = onCellActivate; // 单元格的在激活事件
ctrl.ondeactivate = onCellDeactivate; // 单元格的去激活事件
if ( ctrl._rowspan && ctrl._rowspan > 1 && ctrl.parentElement.rowIndex + 1 < tbl.rows.length ) // 如果需要合并单元格,则合并之
{
for( var n = 1; n < ctrl._rowspan; n++ ) // 合并
{
if ( ctrl.cellIndex < tbl.rows[ctrl.parentElement.rowIndex + 1].cells.length )
tbl.rows[ctrl.parentElement.rowIndex + 1].deleteCell(ctrl.cellIndex);
}
ctrl.rowSpan = ctrl._rowspan; // 设置跨度
}
}
}
}
}
document.onreadystatechange = function() // 在页面加载完成后,初始化TABLE
{
if ( this.readyState == 'complete' )
{
initTable(); // 初始化
}
}
function initTable()
{
for(var idx in document.worksheet ) // 遍历所有表
{
var tbl = $get(document.worksheet[idx].table); // 获取TABLE对象
for( var i = tbl.cells.length - 1; i > 1; i-- ) // 从后往前遍历所有单元格
{
var ctrl = tbl.cells[i]; // 单元格对象
if ( ctrl.cellIndex > 0 && ctrl.parentElement.rowIndex > 0) // 第一行和第一列不处理
{
ctrl.onactivate = onCellActivate; // 单元格的在激活事件
ctrl.ondeactivate = onCellDeactivate; // 单元格的去激活事件
if ( ctrl._rowspan && ctrl._rowspan > 1 && ctrl.parentElement.rowIndex + 1 < tbl.rows.length ) // 如果需要合并单元格,则合并之
{
for( var n = 1; n < ctrl._rowspan; n++ ) // 合并
{
if ( ctrl.cellIndex < tbl.rows[ctrl.parentElement.rowIndex + 1].cells.length )
tbl.rows[ctrl.parentElement.rowIndex + 1].deleteCell(ctrl.cellIndex);
}
ctrl.rowSpan = ctrl._rowspan; // 设置跨度
}
}
}
}
}注意到document.worksheet对象没有?这个对象在生成表格时添加并填充,包括所有的工作表名字和表格id。其格式如下:
view plaincopy to clipboardprint?
document.worksheet = { 'table': null, // HTML table的id ,
'name': '' // 工作表名字
};
document.worksheet = { 'table': null, // HTML table的id ,
'name': '' // 工作表名字
};要使方向键起作用,需要为编辑框添加按键事件处理:
view plaincopy to clipboardprint?
// 编辑框的按键事件
function onTextBoxKeydown()
{
var sender = event.srcElement, // 事件属性
code = event.keyCode;
if ( sender.parentElement &&
sender.parentElement.tagName == 'TD')
{
var tbl = sender.parentElement.parentElement.parentElement, // 表格对象
col = sender.parentElement.cellIndex, // 列号
row = sender.parentElement.parentElement.rowIndex; // 行号
switch(code) // 处理按键代码
{
case 37: // left
{
sender.onchange(); // 手动激发改动事件和在激活事件
if ( col > 1 ) // 第一列是标头,不需要激活
tbl.rows[row].cells[col - 1].onactivate();
}
break;
case 38: // top
{
sender.onchange();
if ( row > 1 )
tbl.rows[row - 1].cells[col].onactivate();
}
break;
case 39: // right
{
sender.onchange();
if ( col < tbl.rows[row].cells.length - 1 )
tbl.rows[row].cells[col + 1].onactivate();
}
break;
case 13:
case 40: // down
{
sender.onchange();
if ( row < tbl.rows.length - 1 )
tbl.rows[row + 1].cells[col].onactivate();
return false;
}
break;
}// switch
}
return true;
}
// 编辑框的按键事件
function onTextBoxKeydown()
{
var sender = event.srcElement, // 事件属性
code = event.keyCode;
if ( sender.parentElement &&
sender.parentElement.tagName == 'TD')
{
var tbl = sender.parentElement.parentElement.parentElement, // 表格对象
col = sender.parentElement.cellIndex, // 列号
row = sender.parentElement.parentElement.rowIndex; // 行号
switch(code) // 处理按键代码
{
case 37: // left
{
sender.onchange(); // 手动激发改动事件和在激活事件
if ( col > 1 ) // 第一列是标头,不需要激活
tbl.rows[row].cells[col - 1].onactivate();
}
break;
case 38: // top
{
sender.onchange();
if ( row > 1 )
tbl.rows[row - 1].cells[col].onactivate();
}
break;
case 39: // right
{
sender.onchange();
if ( col < tbl.rows[row].cells.length - 1 )
tbl.rows[row].cells[col + 1].onactivate();
}
break;
case 13:
case 40: // down
{
sender.onchange();
if ( row < tbl.rows.length - 1 )
tbl.rows[row + 1].cells[col].onactivate();
return false;
}
break;
}// switch
}
return true;
}改动编辑框的内容后,还需要更新单元格内容和持有的数据,这需要处理onchange事件:
view plaincopy to clipboardprint?
// 编辑框的改变事件
function onTextBoxChange()
{
var src = event.srcElement; // 事件属性
if ( src.parentElement &&
src.parentElement.tagName == 'TD' )
{
var val = src.value.trim(); // 去掉空格
if ( val != src.parentElement.dataField ) // 内容有变化?
{ // 更新数据域
src.parentElement.dataField = val;
src.parentElement.value2 = val;
src.parentElement.hasFormula = /=.*/.test(val); // 使用正则表达式判断是否是公式
}
}
}
// 编辑框的改变事件
function onTextBoxChange()
{
var src = event.srcElement; // 事件属性
if ( src.parentElement &&
src.parentElement.tagName == 'TD' )
{
var val = src.value.trim(); // 去掉空格
if ( val != src.parentElement.dataField ) // 内容有变化?
{ // 更新数据域
src.parentElement.dataField = val;
src.parentElement.value2 = val;
src.parentElement.hasFormula = /=.*/.test(val); // 使用正则表达式判断是否是公式
}
}
}好了,现在我们的HTML表格具有类似于Excel的编辑功能了。