您的位置:首页 > Web前端 > HTML

Ajax: Excel风格的HTML Table输入控件

2008-03-11 16:36 447 查看
[一]:内部表格的区域划分与区域标识

实际效果: ]http://www.weiqihome.com/scotttable.jsp

首先需要说明的是,开发过程采用了许多Prototype与scriptaculous的方法和组件。

要实现上下行、左右列均可固定,只有中间行列可滚动的表格,需要将整个表格划分成16个部分,具体如下:
#left fixed columns scrollable columnsright fixed columns
1top fixed rows
2scrollable rows
3bottom fixed rows
这16个单元格内又放置16个表格。其中又有8个单元格又要分别放在8个可滚动的DIV内。
buildFixedTable: function() {
return Builder.node("TABLE", {cellSpacing:'0', cellPadding:'0', style:'table-layout:fixed;border-collapse:collapse;margin:0;'});
},
buildRelativeDiv: function() {
return Builder.node("DIV", {style:'position:relative; overflow:hidden;'});
},
第一个function运用builder创建一个表格,注意table-layou:fixed非常重要。
第二个function运用builder创建一个可滚动的DIV,注意overflow:hidden,因为我们需要提供单独的垂直滚动条,统一负责四个垂直方向的DIV的滚动。也需要提供单独的水平滚动条,统一负责四个水平方向的DIV的滚动。

另外,我们需要给每个区域标注一下。首先,四个列区域分别为no,left,center,right;四个行区域分别为header,top,buffer,footer。这样行列组合成每个区域的标识。例如top行的四个区域的标识分别为:no-top,left-top,center-top,right-top。同时为每个行赋予相应区域的标识。

例如如果要取任一单元格所在行的数据,只需要提供区域名和所在区域的行号即可。实现如下:
getRowData: function(rowIndex, area, rowId) {
var data = new Array(0);
if (rowId == "inserted") {
data[0] = null; //important, can't be ""
} else
data[0] = rowId;
if (area == "footer") {
return data;
}
var getFunction = null;
if (this.leftFixedColumns > 0) {
getFunction = ("_get-left-" + area + "-row-data").camelize();
eval("this." + getFunction + "(rowIndex, data);");
}
getFunction = ("_get-center-" + area + "-row-data").camelize();
eval("this." + getFunction + "(rowIndex, data);");
if (this.rightFixedColumns > 0) {
getFunction = ("_get-right-" + area + "-row-data").camelize();
eval("this." + getFunction + "(rowIndex, data);");
}
return data;
},
这里利用javascript的反射机制调用相应的方法,例如_getLeftTopRowData,_getCenterTopRowData,_getRightTopRowData,_getLeftBufferRowData,_getCenterBufferRowData,_getRightBufferRowData。

同样的方法有很多,如getColCells(取得任一单元格所在列的数据)、getNoCell(取得任一单元格所在行的序号单元格)、getAdjustedColIndex(取得任一单元格所在列的全局列号)、getAdjustedRowIndex(取得任一单元格所在行的全局行号)、setRowCssClass(设置任一单元格所在行的css样式)等。

[二]:外部表格与水平垂直滚动条

外部表格的作用是提供水平垂直滚动条,它们分别与多个可滚动的DIV联系,并且滚动条的大小随着滚动区域的内容动态改变。

先介绍水平垂直滚动区域。前一节介绍了共有7个可滚动区域,它们分别是:centerHeaderBox,centerTopBox,noBufferBox,leftBufferBox,centerBufferBox,rightBufferBox,centerFooterBox;它们分别容纳着7个内部表格:centerHeaderTable,centerTopTable,noBufferTablex,leftBufferTable,centerBufferTable,rightBufferTable,centerFooterTable。

外部表格的构建如下:
buildOuterTable: function() {
this.verticalBar = Builder.node("DIV", {style:'position:relative; overflow:scroll;width:19px;'});
this.heightDiv = document.createElement("div");
this.heightDiv.style.width = "1px";
this.verticalBar.style.height=this.visibleHeight + 18;
this.verticalBar.appendChild(this.heightDiv);
this.verticalBar.onscroll = this.verticalScroll.bindAsEventListener(this);
this.verticalBarBox = Builder.node("DIV", {style:'position:relative; overflow:hidden;width:20px;'});
this.verticalBarBox.style.height=this.visibleHeight;
this.verticalBarBox.appendChild(this.verticalBar);
this.horizonBar = Builder.node("DIV", {style:'position:relative; overflow:scroll;height:19px;'});
this.horizonBar.style.width = this.visibleWidth + 18;
this.horizonBar.appendChild(this.widthDiv);
this.horizonBar.onscroll = this.horizonScroll.bindAsEventListener(this);
this.horizonBarBox = Builder.node("DIV", {style:'position:relative; overflow:hidden;width:20px;'});
this.horizonBarBox.style.width=this.visibleWidth + 2
this.horizonBarBox.appendChild(this.horizonBar);
this.outerTable = this.buildFixedTable();
var row = this.outerTable.insertRow();
var cell = row.insertCell();
cell.style.width = this.visibleWidth + 3;
cell.appendChild(this.innerTable);
cell = row.insertCell();
cell.style.width = "19px";
cell.appendChild(this.verticalBarBox);
row = this.outerTable.insertRow();
var cell1 = row.insertCell();
var cell2 = row.insertCell();
row.removeChild(cell1);
cell2.colSpan = "2";
cell2.appendChild(this.horizonBarBox);
},

可以看出,水平垂直滚动条都有三层:外部Box、中间显示滚动条的DIV、最里面代表实际大小的DIV(另外水平部分的widthDiv在外面创建)。外部Box的style是:overflow:hidden,这样可不显示滚动条多余的部分。当滚动区域的内容即行数或列宽度改变时,只需要改变最里面的DIV的style的width或height即可。
另外请注意,水平垂直滚动条的style是overflow:scroll,这样保证即使不需要滚动时,滚动条依然在。
另外在创建内部表格的同时,也将这另外几个可滚动DIV的onscroll事件与其Scroll方法关联起来,这样任一DIV的滚动导致其他相应DIV同时一致滚动。

滚动实现如下:
horizonScroll: function() {
this.centerBufferBox.scrollLeft = this.horizonBar.scrollLeft;
this.centerHeaderBox.scrollLeft = this.horizonBar.scrollLeft;
if (this.topFixedRows > 0) {
this.centerTopBox.scrollLeft = this.horizonBar.scrollLeft;
}

if (this.bottomFixedRows > 0) {
this.centerFooterBox.scrollLeft = this.horizonBar.scrollLeft;
}

},
verticalScroll: function() {
this.centerBufferBox.scrollTop = this.verticalBar.scrollTop;
this.noBufferBox.scrollTop = this.verticalBar.scrollTop;
if (this.leftBufferBox) {
this.leftBufferBox.scrollTop = this.verticalBar.scrollTop;
}
if (this.rightBufferBox) {
this.rightBufferBox.scrollTop = this.verticalBar.scrollTop;
}
},

centerBufferScroll: function() {
this.verticalBar.scrollTop = this.centerBufferBox.scrollTop;
if (this.leftBufferBox) {
this.leftBufferBox.scrollTop = this.verticalBar.scrollTop;
}
if (this.rightBufferBox) {
this.rightBufferBox.scrollTop = this.verticalBar.scrollTop;
}
},
leftBufferScroll: function() {
this.verticalBar.scrollTop = this.leftBufferBox.scrollTop;
this.centerBufferBox.scrollTop = this.verticalBar.scrollTop;
if (this.rightBufferBox) {
this.rightBufferBox.scrollTop = this.verticalBar.scrollTop;
}
},
rightBufferScroll: function() {
this.verticalBar.scrollTop = this.rightBufferBox.scrollTop;
this.centerBufferBox.scrollTop = this.verticalBar.scrollTop;
if (this.leftBufferBox) {
this.leftBufferBox.scrollTop = this.verticalBar.scrollTop;
}
}
[三]:线条与滚动条的精确控制

第一节介绍了内部表格,共有16个区域,每个区域内都是单独的表格。如果每个表格都设置同样的线条,那么表格相邻的两条线会重叠形成粗线条,让人感觉不好。而表格之间必须要有线条指簦蚁咛豸于哪个表格,运行效果也不同?lt;/div>

例如,水平滚动时,固定区域与可滚动区域之间有一根线条,如果这根线条属于滚动区域,那么滚动时,此线条可能不在可视区内,导致固定区域与滚动区域之间没有线条。所以此线条应该属于固定区域。

现在我们应该清楚,整个表格控件,其表格层次又有三层:外部表格、内部表格、区域表格。除外部没有线条外,如何协调好内部表格与区域表格的线条,对整个表格的可视效果非常重要。

因为整个控件外部肯定要有个线条,所以内部表格外面由一矩形线条包围,这样区域表格与内部表格相邻的部分不能有线条。固定区域与滚动区域的线条属于固定区域,滚动区域内的区域表格内部依然要有线条。当你在滚动区域内增加一行到末尾时,要补上上一行的下部线条,同时去掉最后一行的下部线条;同理当删除最后一行时,要去掉前一行的下部线条。如果表格行很少时,还要保留该线条。

对于水平垂直滚动条的宽度和高度,也要将线条考虑进去。否则导致滚动条的滚动范围与实际滚动区域的宽度或高度不一致。

在每增加一行或删除一行时:
this.heightDiv.style.height = this.visibleHeight + (this.centerBufferTable.rows.length) * (this.rowHeight+1) - this.centerBufferBox.offsetHeight;

[四]:事件与单元格的遍历

本节介绍表格控件的事件处理,以及如何用键盘和鼠标遍历单元格,以及如何编辑、取消单元格的编辑。

1. 鼠标事件
首选取得当前单元格:
this.lastSelectedCell = Event.findElement(ev, "TD");
this.lastSelectedRow = this.lastSelectedCell.parentNode;

对每一个区域表格都注册鼠标单击事件与双击事件:
this.centerBufferTable.onclick = this.onClick.bindAsEventListener(this);
this.centerBufferTable.ondblclick = this.editCell.bindAsEventListener(this);
这里注册当双击时,就编辑单元格。如何编辑单元格将在第五节介绍。
当单击时,就选中当前单元格,同时不选中原来选中的单元格:
Element.removeClassName(this.lastSelectedCell, "cellselected");
Element.addClassName(this.lastSelectedCell, "cellselected");
同时,设置

另外还要考虑,如果是checkbox或radio风格的单元格,当单击时就改变其状态。

2. 键盘事件
Event.observe(document, 'keypress', this.onKeyPress.bindAsEventListener(this));
这里统一设置键盘事件,而不是每个区域表格都注册。

首先处理键盘事件时,使事件停止,不致再往下传播。
Event.stop(ev);很重要,不然当编辑单元格时,事件会传播到编辑控件。

同鼠标事件的处理,当前后左右遍历单元格时,就选中当前单元格,同时不选中原来选中的单元格。具体算法:
//UP & DOWN
if(ev.keyCode==40 || ev.keyCode==38){
if (ev.keyCode==38) {
if (this.lastSelectedRow.rowIndex > 0) {
Element.removeClassName(this.lastSelectedCell, "cellselected");
this.lastSelectedRow = this.lastSelectedRow.parentNode.rows[this.lastSelectedRow.rowIndex-1];
this.lastSelectedCell = this.lastSelectedRow.cells[this.lastSelectedCell.cellIndex];
Element.addClassName(this.lastSelectedCell, "cellselected");
}
}
if (ev.keyCode==40) {
if (this.lastSelectedRow.rowIndex < this.lastSelectedRow.parentNode.rows.length-1) {
Element.removeClassName(this.lastSelectedCell, "cellselected");
this.lastSelectedRow = this.lastSelectedRow.parentNode.rows[this.lastSelectedRow.rowIndex+1];
this.lastSelectedCell = this.lastSelectedRow.cells[this.lastSelectedCell.cellIndex];
Element.addClassName(this.lastSelectedCell, "cellselected");
}
}
}

当回车事件时,如果原来是编辑状态,则停止编辑,否则编辑当前单元格。
//ENTER
else if(ev.keyCode==13) {
if (this.editor != null)
this.editStop(true);
else
this.editCell();
}
TAB键和左右是相关的:
//RIGHT LEFT
else if(ev.keyCode==37 || ev.keyCode==39){
if (ev.keyCode==37) {
navLeft = true;
}
if (ev.keyCode==39) {
navRight = true;
}
}
//TAB
else if(ev.keyCode==9 && !ev.shiftKey) {
navRight = true;
if (lastEditing) {
nextEditing = true;
}
}
else if(ev.keyCode==9 && ev.shiftKey) {
navLeft = true;
if (lastEditing) {
nextEditing = true;
}
}
F2键编辑当前单元格:
//F2
else if(ev.keyCode==113) {
this.editCell();
}
ESC键取消编辑:
//ESC
else if(ev.keyCode==27) {
this.editStop(false);
}
SPACE键也是编辑当前单元格:
//SPACE
else if(ev.keyCode==32) {
if (this.editor == null) {
this.editCell();
}
}

3. 使当前单元格始终在可视区
当用键盘或鼠标遍历单元格时,如果当前单元格部分或全部不在可视区时,必须滚动使其在可视区内,同时协调相应的其他的区域滚动。
var element = this.lastSelectedCell;
var x = element.x ? element.x : element.offsetLeft;
var y = element.y ? element.y : element.offsetTop;
var divParent = this.lastSelectedRow.parentNode.parentNode.parentNode;
if (divParent.scrollTop > y) {
this.verticalBar.scrollTop = y;
this.verticalScroll();
}
if (divParent.scrollTop + divParent.offsetHeight < y + element.offsetHeight) {
this.verticalBar.scrollTop = y + element.offsetHeight - divParent.offsetHeight;
this.verticalScroll();
}
if (divParent.scrollLeft > x) {
this.horizonBar.scrollLeft = x;
this.horizonScroll();
}
if (divParent.scrollLeft + divParent.offsetWidth < x + element.offsetWidth) {
this.horizonBar.scrollLeft = x + element.offsetWidth - divParent.offsetWidth;
this.horizonScroll();
}
[五]:单元格的编辑

从本节开始将重点介绍单元格的编辑、显示、数据的修改及保存到服务器端。

首先要说明的是,单元格的实际值、编辑、显示是不同的。例如checkbox和radio的编辑和显示是一样的;price的编辑和显示是不一样的,因为显示时可能还带币种。
实际值:cell.value
编辑类:Editor_xx
显示类:Render_xx

前一节介绍当回车、F2、SPACE、双击时均可导致编辑单元格。
editCell: function(fl) {
if (this.editor != null) this.editStop(true);
var c = this.lastSelectedCell;
eval("this.editor = new Editor_" + this.editTypes[this.getAdjustedColIndex(c)] + "(c, this);");
if (!fl && this.editor.changeState()) {
this.cellUpdated();
this.editor=null;
this.renderCell(this.lastSelectedCell);
} else {
Element.removeClassName(c, "editable");
this.editor.edit();
}
},
editStop: function(fl) {
if(this.editor!=null){
Element.removeClassName(this.lastSelectedCell, "editable");
this.editor.detach(fl);
this.editor=null;
}
this.renderCell(this.lastSelectedCell);
},
这里同样利用反射机制,自动实例化不同的编辑类。

首先看看基类的定义:
Editor_base.prototype = {
detach: function(fl) {
if (this.obj && this.obj.parentNode) {
this.obj.parentNode.removeChild(this.obj);
}
},

setValue: function(val) {
if (val != this.cell.value) {
this.cell.value = val;
this.grid.cellUpdated();
}
},

onKeyPress: function(e){
(e||event).cancelBubble = true;
if ((e||event).keyCode==13) {
this.grid.editStop(true);
} else if(e.keyCode==27) {
this.grid.editStop(false);
} else if(e.keyCode==9) {
this.grid.onKeyPress(e);
}
},

getValue: function() {
return this.cell.value;
},
changeState: function() {
return false;
}
}
detach方法使去除编辑控件,使单元格重新处于显示状态。
setValue判断值是否改变,如果改变激发cellUpdated事件。
onKeyPress监听编辑控件的事件,如果回车、ESC或TAB时,则停止编辑,且TAB时,自动使下一个单元格处于编辑状态。注意textarea需要重新定义该方法,因为其回车是换行,只有同时shift或ctrl处于按下状态,才编辑停止。
getValue则是取得单元格的实际值。
changeState则可以直接改变单元格的状态。

另外每个控件都有edit方法,因为每个控件的编辑控件不一样。
1. 只读列的edit
Editor_read = Class.create();
Object.extend(Object.extend(Editor_read.prototype, Editor_base.prototype), {
initialize: function(cell, grid) {
this.cell = cell;
this.grid = grid;
},
edit: function() {}
});

2. 简单文本编辑
Editor_text = Class.create();
Object.extend(Object.extend(Editor_text.prototype, Editor_base.prototype), {
initialize: function(cell, grid) {
this.cell = cell;
this.grid = grid;
},
edit: function() {
this.val = this.getValue();
this.obj = document.createElement("INPUT");
this.obj.style.height = (this.cell.offsetHeight-7)+"px";
this.obj.style.width = (this.cell.offsetWidth-7)+"px";
this.obj.className="dhx_combo_edit";
this.obj.wrap = "soft";
this.obj.style.textAlign = "left";
this.obj.onclick = function(e){(e||event).cancelBubble = true}
this.obj.onkeydown = this.onKeyPress.bindAsEventListener(this);
this.obj.value = this.val;
this.cell.innerHTML = "";
this.cell.appendChild(this.obj);
this.obj.onselectstart=function(e){ if (!e) e=event; e.cancelBubble=true; return true; };
this.obj.focus();this.obj.focus();
},
detach: function(fl) {
if (fl) {
this.setValue(this.obj.value);
}
if (this.obj.parentNode) {
this.obj.parentNode.removeChild(this.obj);
}
}
});
这里是直接使编辑控件占用单元格的空间。

3. 多行textarea的编辑
Editor_textarea = Class.create();
Object.extend(Object.extend(Editor_textarea.prototype, Editor_base.prototype), {
initialize: function(cell, grid) {
this.cell = cell;
this.grid = grid;
},
edit: function() {
this.val = this.getValue();
this.obj = document.createElement("TEXTAREA");
this.obj.className="dhx_textarea";
var arPos = Util.getPosition(this.cell);
//this.obj.value = this.cell.innerHTML.replace(/<br[^>]*>/gi,"/n");
document.body.appendChild(this.obj);
this.obj.style.left = arPos[0]-Position.realOffset(this.cell)[0]+"px";
this.obj.style.top = arPos[1]+this.cell.offsetHeight-Position.realOffset(this.cell)[1]+"px";
if(this.cell.scrollWidth<200)
this.obj.style.width = "200px";
else
this.obj.style.width = this.cell.scrollWidth+"px";
this.obj.style.display = "";
this.obj.focus();
this.obj.onclick = function(e){(e||event).cancelBubble = true}
this.obj.onkeydown = this.onKeyPress.bindAsEventListener(this);
this.obj.value = this.val;
//this.cell.appendChild(this.obj);
this.obj.onselectstart=function(e){ if (!e) e=event; e.cancelBubble=true; return true; };
this.obj.focus();this.obj.focus();
this.obj.value = this.val;
},
onKeyPress: function(e){
(e||event).cancelBubble = true;
if ((e||event).keyCode==13 && (e.altKey || e.shiftKey)) {
this.grid.editStop(true);
} else if(e.keyCode==27) {
this.grid.editStop(false);
} else if(e.keyCode==9) {
this.grid.onKeyPress(e);
}
},

detach: function(fl) {
if (fl) {
var val = this.obj.value.replace(//n/g,"<br/>");
this.setValue(val);
}
document.body.removeChild(this.obj);
},
getValue: function() {
return this.cell.value.replace(/<br[^>]*>/gi,"/n")
}

});
注册显示textarea的位置,必须先确定当前单元格的位置。
getPosition: function(oNode,pNode) {
if(!pNode)
var pNode = document.body

var oCurrentNode=oNode;
var iLeft=0;
var iTop=0;
while ((oCurrentNode)&&(oCurrentNode!=pNode)) {
iLeft+=oCurrentNode.offsetLeft;
iTop+=oCurrentNode.offsetTop;
oCurrentNode=oCurrentNode.offsetParent;//isIE()?:oCurrentNode.parentNode;
}
return new Array(iLeft,iTop);
}

4. checkbox和radio的编辑则是直接改变状态
对checkbox:
changeState: function() {
if (this.cell.value > "0") {
this.cell.value = "0";
} else
this.cell.value = "1";
return true;
}
对radio:
changeState: function() {
if (this.cell.value <= "0") {
this.cell.value = "1";
return true;
}
return false;
}
注意,如果radio单元格处于选中状态,只有选择同列的其他单元格,才能改变其值。
这个是在cellUpdated方法中单独处理的:
if (editType == "radio" && this.radioUnique) {
if (cv > 0) {
var cellIndex = c.cellIndex;
var cells = this.getColCells(cellIndex, r.area.split("-")[0]);
for (var i=0;i<cells.length;i++) {
var tc = cells[i];
if (tc != c) {
tc.value = uv;
this.renderCell(tc);
}
}
}
}

5. 最后是combo或select的编辑
Editor_combo = Class.create();
Object.extend(Object.extend(Editor_combo.prototype, Editor_base.prototype), {
initialize: function(cell, grid) {
this.cell = cell;
this.grid = grid;
this.combo = this.grid.getCombo(this.cell.cellIndex);
this.editable = true
},

edit: function() {
this.val = this.getValue();
var arPos = Util.getPosition(this.cell)//,this.grid.objBox)
this.obj = document.createElement("INPUT");
this.obj.className="dhx_combo_edit";
this.obj.style.height = (this.cell.offsetHeight-7)+"px";
this.obj.style.width = (this.cell.offsetWidth-7)+"px";
this.obj.wrap = "soft";
this.obj.style.textAlign = this.cell.align;
this.obj.onclick = function(e){(e||event).cancelBubble = true}
this.obj.value = this.val
this.list = document.createElement("SELECT");
this.list.editor_obj = this;
this.list.className='dhx_combo_select';
document.body.appendChild(this.list)//nb:this.grid.objBox.appendChild(this.listBox);
this.list.style.width=this.cell.offsetWidth+"px";
this.list.style.left = arPos[0]-Position.realOffset(this.cell)[0]+"px";//arPos[0]
this.list.style.top = arPos[1]+this.cell.offsetHeight-Position.realOffset(this.cell)[1]-4+"px";//arPos[1]+this.cell.offsetHeight;
this.list.size="6";
this.list.onclick = function(e){
var ev = e||window.event;
var cell = ev.target||ev.srcElement
//tbl.editor_obj.val=cell.combo_val;
if (cell.tagName=="OPTION") cell=cell.parentNode;
cell.editor_obj.setValue(cell.value);
cell.editor_obj.editable=false;
cell.editor_obj.detach();
}
var comboKeys = this.combo.getKeys();
var fl=false
var selOptId=0;
for(var i=0;i<comboKeys.length;i++){
var val = this.combo.get(comboKeys[i])
this.list.options[this.list.options.length]=new Option(val,comboKeys[i]);
if(comboKeys[i]==this.val){
selOptId=this.list.options.length-1;
fl=true;
}
}
if(fl==false) {//if no such value in combo list
this.list.options[this.list.options.length]=new Option(this.val,this.val===null?"":this.val);
selOptId=this.list.options.length-1;
}
this.cstate=1;
if(this.editable){
this.cell.innerHTML = "";
} else {
this.obj.style.width="1px";
this.obj.style.height="1px";
}
this.obj.onkeydown = this.onKeyPress.bindAsEventListener(this);
this.cell.appendChild(this.obj);
this.list.options[selOptId].selected=true;
this.obj.focus();
this.obj.focus();
if (!this.editable) {
this.obj.style.visibility="hidden";
this.list.focus();
this.list.onkeydown = this.onKeyPress.bindAsEventListener(this);
}
},
onKeyPress: function(e){
(e||event).cancelBubble = true;
if ((e||event).keyCode==13) {
this.grid.editStop(true);
}
else if ((e||event).keyCode==40) {
if (this.list.selectedIndex < this.list.options.length - 1) {
this.list.selectedIndex = this.list.selectedIndex + 1;
}
this.obj.value = this.list.options[this.list.selectedIndex].value;
}
else if ((e||event).keyCode==38) {
if (this.list.selectedIndex > 0) {
this.list.selectedIndex = this.list.selectedIndex - 1;
this.obj.value = this.list.options[this.list.selectedIndex].value;
}
} else if(e.keyCode==27) {
this.grid.editStop(false);
} else if(e.keyCode==9) {
this.grid.onKeyPress(e);
}
},
detach: function(fl) {
if (fl) {
var val = null
if(this.list.parentNode!=null){
if (this.editable)
if(this.obj.value!=this.text) {
val = this.obj.value;
} else {
val = this.val;
}
else
val = this.list.value;
}
this.setValue(val);
}
if(this.list.parentNode)
this.list.parentNode.removeChild(this.list);
if(this.obj.parentNode)
this.obj.parentNode.removeChild(this.obj);
}
});
Editor_list = Class.create();
Object.extend(Object.extend(Editor_list.prototype, Editor_combo.prototype), {
initialize: function(cell, grid) {
this.cell = cell;
this.grid = grid;
this.combo = this.grid.getCombo(this.cell.cellIndex);
this.editable = false
}
});
这里结合了前面input和textarea的定位方法。
[六]、单元格的显示

本节介绍如何显示单元格,包括如何以text,checkbox,radio来显示单元格。

Render_base = Class.create();
Render_base.prototype = {
initialize: function(grid) {
this.grid = grid;
},
getRenderHTML: function(val) {}
}
Render_text = Class.create();
Object.extend(Object.extend(Render_text.prototype, Render_base.prototype), {
getRenderHTML: function(val) {
if (val == null || val == "") {
return " ";
}
return val;
}
});
Render_check = Class.create();
Object.extend(Object.extend(Render_check.prototype, Render_base.prototype), {
getRenderHTML: function(val) {
if (val == "") {
val = "0";
}
if (val == "1" || val == "0") {
return "<img src='"+this.grid.imgURL+"item_chk"+val+".gif'>";
} else if (val == null) {
return " ";
} else
return val;
}
});
Render_radio = Class.create();
Object.extend(Object.extend(Render_radio.prototype, Render_base.prototype), {
getRenderHTML: function(val) {
if (val == "") {
val = "0";
}
if (val == "1" || val == "0") {
return "<img src='"+this.grid.imgURL+"radio_chk"+val+".gif'>";
} else if (val == null) {
return " ";
} else
return val;
}
});

可见,只要实现不同的getRenderHTML就可以了。
可以很方便的扩展其他显示类,如price,color等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: