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

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的定位方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: