Ajax: Excel风格的HTML Table输入控件[五]:单元格的编辑
2006-12-19 23:21
381 查看
实际效果: http://www.weiqihome.com/scotttable.jsp
从本节开始将重点介绍单元格的编辑、显示、数据的修改及保存到服务器端。
首先要说明的是,单元格的实际值、编辑、显示是不同的。例如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的定位方法。
从本节开始将重点介绍单元格的编辑、显示、数据的修改及保存到服务器端。
首先要说明的是,单元格的实际值、编辑、显示是不同的。例如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的定位方法。
相关文章推荐
- Ajax: Excel风格的HTML Table输入控件[四]:事件与单元格的遍历
- Ajax: Excel风格的HTML Table输入控件[六]:单元格的显示
- Ajax: Excel风格的HTML Table输入控件[一]:内部表格的区域划分与区域标识
- Ajax: Excel风格的HTML Table输入控件[三]:线条与滚动条的精确控制
- Ajax: Excel风格的HTML Table输入控件[二]:外部表格与水平垂直滚动条
- Ajax: Excel风格的HTML Table输入控件
- EXCEL VBA代码,实现点击Sheet1按钮控件保存不连续单元格的数据到Sheet2中,然后清空输入内容
- ajax模仿Excel,实现双击GridView单元格保存编辑
- 直接编辑Excel 单元格,而不需双击才能输入
- DataExcel 设置单元格 编辑控件 radiocheck,checkbox,textedit,combotext,numberedit
- js把html中的table数据导出到Excel中
- telerik 的RadGridVIew控件 单元格无法编辑
- ajax 获取当前 html输入 已经返回后台php处理文件
- 在TableViewer中如何给表格的单元格增加其他组件的编辑功能
- html编辑tinymce在ajax情况下的使用
- ajax 编辑保存table
- Table 控件单元格的动态合并技术 .
- ApachePOI导出exce,设置单元格风格的属性和设置字体风格的属性的两个工具类,POI宽度和excel 像素转换
- 可编辑的 HTML JavaScript 表格控件 DataGrid
- html treeTable ajax 动态加载