<html>
<head>
<style type="text/css">
.grid{border:1px solid #808080; border-spacing:0; width:500px; border-collapse:collapse}
.grid th,.grid td{border:0; text-align:center;}
.grid tr{height:25px;line-height:25px;}
.grid tr.odd{background:#d0d0d0}
.grid .btn{width:80px; text-align:center}
</style>
<script type="text/javascript">
(function(){
//
var util = {
next:function next(ele){
if(ele){
var n = ele.nextSibling;
if(n && n.nodeType === 1){
return n;
}
return next(n);
}
},
parseJSON:function(str){
if(typeof JSON !== "undefined"){
return JSON.parse(str);
}
return eval("("+str+")");
},
parseArray:function(obj){
if(!obj){
return obj;
}
var result = [];
if(typeof obj.length !== "undefined"){
try{
var arr = Array.prototype.slice.call(obj,0);
result = arr;
}catch(e){
for(var i=0;i<obj.length;i++){
try{
var target = obj[i];
}catch(e){
if(obj.item){
var target = this.item(i);//nodeList
}
}
if(typeof target !== "undefined"){
result.push(target);
delete target;
}
}
}
}
return result;
},
isFunction:function(fn){
return typeof fn === "function";
},
trim:function(str){
if(typeof str !== "string"){
return str;
}
return str.replace(/^\s+|\s+$/g,"");
},
offset:function offset(ele){
var result = {top:0,left:0};
if(!ele || ele.nodeType !== 1){
return result;
}
result.top = Number(ele.offsetTop || (ele.style.top || "0").replace(/[^\d]+$/,""));
result.left = Number(ele.offsetLeft || (ele.style.left || "0").replace(/[^\d]+$/,""));
if(ele.parentNode){
var r = offset(ele.parentNode);
result.top += r.top;
result.left += r.left;
}
return result;
}
};
//事件处理
var Event = {
on:function(eventType,fn){
var element = this;
if(this.addEventListener){
this.addEventListener(eventType,fn,false);
}else if(this.attachEvent){
//将事件缓冲到该标签上,已解决this指向window(现fn内this指向element)和移除匿名事件问题
var _EventRef='_'+eventType+'EventRef';
if(!element[_EventRef]){
element[_EventRef]=[];
}
var _EventRefs=element[_EventRef];
var index;
for(index in _EventRefs){
if(_EventRefs[index]['realFn']==fn){
return;
}
}
var nestFn=function(){
fn.apply(element,arguments);
};
element[_EventRef].push({'realFn':fn,'nestFn':nestFn});
element.attachEvent('on'+eventType,nestFn);
}
},
remove:function(eventType,fn){
var element = this;
if(this.removeEventListener){
this.removeEventListener(eventType,fn,false);
}else if(this.detachEvent){
var _EventRef='_'+eventType+'EventRef';
if(!element[_EventRef]){
element[_EventRef]=[];
}
var _EventRefs=element[_EventRef]
var index;
var nestFn;
for(index in _EventRefs){
if(_EventRefs[index]['realFn']==fn){
nestFn=_EventRefs[index]['nestFn'];
if(index==_EventRefs.length-1){
element[_EventRef]=_EventRefs.slice(0,index);
}else{
element[_EventRef]=_EventRefs.slice(0,index).concat(_EventRefs.slice(index+1,_EventRefs.length-1));
}
break;
}
}
if(nestFn){
element.detachEvent('on'+eventType,nestFn);
}
}
}
};
//extend
(function(){
//删除数组中指定下标出的元素
Array.prototype.remove = function(index){
var o = this[index];
if(typeof o !== "undefined"){
if(index == 0){
this.shift();
}else if(index === this.length - 1){
this.pop();
}else{
var arr1 = this.slice(0,index);
var arr2 = this.slice(index+1);
this.length = 0;
this.concat(arr1,arr2);
}
}
}
//删除数组中所有的元素
Array.prototype.clear = function(){
this.length = 0;
}
//each
Array.prototype.each = function(fn){
if(!util.isFunction(fn)){
return;
}
for(var i=0;i<this.length;i++){
if(typeof this[i] !== "undefined"){
fn.call(this[i],i);
}
}
}
//
var collection = this.collection = function(){
this.__data = {};
this.length = 0;
}
collection.prototype = {
add:function(obj){
this.__data[this.length++] = obj;
},
get:function(index){
return this.__data[index];
},
remove:function(index){
var obj = this.__data[index];
if(typeof obj !== "undefined"){
this.length--;
}
delete this.__data[index];
},
clear:function(){
this.__data = {};
this.length = 0;
},
each:function(fn){
var index = 0;
for(var k in this.__data){
if(k && typeof this.__data[k] !== "undefined"){
fn.call(this.__data[k],index++);
}
}
},
toArray:function(){
var arr = [];
this.each(function(){
arr.push(this);
});
return arr;
}
};
})();
//
var grid = this.grid = function(table, options){
grid.prototype._init.call(this,table,options);
}
grid.prototype = {
_init:function(table, options){
if(typeof table === "undefined" || !table){
throw "table is undefined or null";
}
if(table.nodeType !== 1 || !/^table$/i.test(table.tagName)){
throw "table must be 'table' element.";
}
table.guid = ++grid.guid;
this.__cache = {};
var self = this;
var header = this.__cache["header"] = loadHeader();//header
this.__root = header.parentNode;
var templateRow = this.__cache["template"] = loadTemplate();//模板行
this.__cache["dataFormat"] = loadDataFormat();//数据模板
this.__cache["dataRows"] = new collection();//数据行
this.__cache["customCache"] = new collection();//用户缓存数据
this.__editRow = null;//当前编辑行
this.__saveContainer = createSaveButton();//保存按钮
this.__cache["commandHandles"] = {//command handels
removeRow:function(){
var rowIndex = this.getAttribute("index");
self.removeRow.apply(self,[rowIndex]);
},
newLine:function(){
self.newLine();
}
};
this.__regCommand = function(commandName,row){ //注册command
if(row){
var arg = row.getAttribute("index");
this.setAttribute("index",arg || false);
}
this.commandName = commandName;
Event.remove.call(this,"click",exec);
Event.on.call(this,"click",exec);
}
this.__removeRowCallback = function(){ //改变行的背景样式
var rows = this.__cache["dataRows"];
var customCache = this.__cache["customCache"];
var arr = rows.toArray(),dataArr=[];
var rowIndex,row,data;
rows.clear();
arr.each(function(i){
rowIndex = this.getAttribute("index");
data = customCache.get(rowIndex);
dataArr.push(data);
this.setAttribute("index",i.toString());
rows.add(this);
if( i % 2 == 1){//基数行
if(!/\sodd\s|\sodd$/g.test(this.className)){
this.className = (this.className || "") + " odd";
}
}else if(/\sodd\s|\sodd$/g.test(this.className)){
this.className = this.className.replace(/\sodd\s|\sodd$/g," ");
}
i++;
});
customCache.clear();
dataArr.each(function(){
customCache.add(this);
});
}
//事件处理
options = options || {};
this.onDataBinding = options.onDataBinding || this.onDataBinding;
this.onRowBinding = options.onRowBinding || this.onRowBinding;
this.onRowBinded = options.onRowBinded || this.onRowBinded;
function loadHeader(){
var tr = table.firstChild;
if(tr && tr.nodeType != 1){
tr = util.next(tr);
}
if(!/tr/i.test(tr.tagName)){ //如果第一个元素不是tr,则浏览器支持tbody
tr = tr.firstChild;
if(tr.nodeType != 1){
tr = util.next(tr);
}
}
return tr;
}
function loadTemplate(){
tr = util.next(header);//获取模板行
return tr;
}
function loadDataFormat(){
var nodes = templateRow.childNodes,ele,data,result = {},attr;
nodes = util.parseArray(nodes);
nodes.each(function(i){
ele = this;
if(ele && ele.nodeType == 1){
attr = ele.data || ele.getAttribute("data");
if(attr){
data = util.parseJSON(attr);
ele.field = data.field;
result[ele.field] = data;
}
}
});
return result;
}
function createSaveButton(){
var div = document.createElement("div");
div.style.position = "absolute";
div.style.display = "none";
div.style.width = "auto";
var btn = document.createElement("button");
btn.innerHTML = btn.innerText = btn.textContent = btn.value = "Save";
try{
btn.type = "button";
}catch(e){
btn.setAttribute("type","button");
}
div.appendChild(btn);
var btnCancel = document.createElement("button");
btnCancel.innerHTML = btnCancel.innerText = btnCancel.textContent = btnCancel.value = "Cancel";
try{
btnCancel.type = "button";
}catch(e){
btnCancel.setAttribute("type","button");
}
div.appendChild(btnCancel);
document.body.appendChild(div);
Event.on.call(btn,"click",function(){
self.save();
});
Event.on.call(btnCancel,"click",function(){
div.style.display = "none";
if(self.__editRow){
self.__editRow.parentNode.removeChild(self.__editRow);
self.__editRow = null;
}
});
return div;
}
function exec(){
if(self.__editRow){//如果当前处于编辑模式,则禁用所有command
return;
}
var commandName = this.commandName;
var handler = self.__cache["commandHandles"][commandName];
if(handler){
handler.call(this);
}
}
//去除模板行
templateRow.parentNode.removeChild(templateRow);
//处理表格中的command事件
var elements = header.getElementsByTagName("*");
elements = util.parseArray(elements);
elements.each(function(){
if(this.nodeType === 1){
var commandName = this.command || this.getAttribute("command");
if(commandName){
self.__regCommand.call(this,commandName,header);
}
}
});
},
//bangding
bind:function(data){
this.clear();
if(data && data.length > 0){
var self = this;
data.each(function(){
self.append(this);
});
}
},
//清理表,删除所以除header以外的数据行
clear:function(){
var rows = this.__cache["dataRows"],row;
rows.each(function(){
row = this;
if(row){
row.parentNode.removeChild(row);
}
});
rows.clear();//清理rows
},
//删除指定的行
removeRow:function(rowIndex){
var rows = this.__cache["dataRows"];
var row = rows.get(rowIndex);
if(row){
var data = this.__cache["customCache"][rowIndex];
row.parentNode.removeChild(row);
rows.remove(rowIndex);
//通知用户数据行被移除
if(util.isFunction(this.onRowRemoved)){
this.onRowRemoved(data,row);
}
}
this.__removeRowCallback();
},
//添加 行
append:function(data){
if(!data){
return ;
}
var template = this.__cache["template"];
var rows = this.__cache["dataRows"];
var rowIndex = rows.length;
var tr = template.cloneNode();
var customCache = this.__cache["customCache"];
customCache.add(data);
//将数据行添加到table
this.__root.appendChild(tr);
var self = this;
var td,//数据单元格
dataFormat,//数据格式化器
value;//单元格中的给定的数据
tr.setAttribute("index",rowIndex.toString());
//更改样式
if(rowIndex % 2 == 1){
tr.className = (tr.className || "") + " odd";
}
//通知 行数据绑定开始
if(util.isFunction(this.onRowBinding)){
this.onRowBinding(rowIndex,tr);
}
var templateTD = template.firstChild;
while(templateTD){
td = templateTD.cloneNode(true);
tr.appendChild(td);
if(td.nodeType == 1 && templateTD.field){
dataFormat = this.__cache["dataFormat"][templateTD.field];
td.removeAttribute("data");
td.field = templateTD.field;
value = data[dataFormat.field];
//通知单元格数据绑定事件
value = this.onDataBinding(dataFormat.field,value,td,data);
if(value !== false){//如果返回false,则不用做赋值操作
switch(dataFormat.render){
case "innerHTML":
td.innerHTML = typeof value == "undefined" || value == null ? "" : value;
break;
case "innerText":
default:
td.innerText = td.textContent = typeof value == "undefined" || value == null ? "" : value;
break;
}
}
}
templateTD = templateTD.nextSibling;
}
rows.add(tr);
//处理command
var elements = tr.getElementsByTagName("*"),ele,attr;
elements = util.parseArray(elements);
elements.each(function(){
ele = this;
if(ele.nodeType == 1 && (ele.command || ele.getAttribute("command"))){
attr = ele.command || ele.getAttribute("command");
self.__regCommand.call(ele,attr,tr);
}
});
//通知 行数据绑定完成
if(util.isFunction(this.onRowBinded)){
this.onRowBinded(rowIndex,tr);
}
},
//手动产生新的输入行
newLine:function(){
if(this.__editRow){//如果当前有存在编辑行,则直接返回,每次最多限制编辑一行数据
return;
}
var template = this.__cache["template"] ;
var row = this.__editRow = template.cloneNode(false);
var templateTD = template.firstChild;
var textareaList = [];
while(templateTD){
td = templateTD.cloneNode(true);
row.appendChild(td);
if(td.nodeType == 1){
if(templateTD.field){
td.field = templateTD.field;
td.innerHTML = "";
var dataFormat = this.__cache["dataFormat"][templateTD.field];
var textarea = null;
if(dataFormat.render == "innerHTML"){
textarea = document.createElement("textarea");
}else{
textarea = document.createElement("input");
textarea.type = "text";
}
textarea.style.display = "none";
td.appendChild(textarea);
textareaList.push(textarea);
}
}
templateTD = templateTD.nextSibling;
}
//将数据行添加到table
this.__root.appendChild(row);
var height = row.offsetHeight,
width = row.offsetWidth,
offset = util.offset(row);
textareaList.each(function(){
this.style.height = (0.8 * height) + "px";
this.style.width = (0.8 * this.parentNode.offsetWidth) + "px";
this.style.display = "";
});
var left = offset.left + width + 5;
var top = offset.top;
this.__saveContainer.style.top = top + "px";
this.__saveContainer.style.left = left + "px";
this.__saveContainer.style.height = this.__saveContainer.style.lineHeight = height + "px";
this.__saveContainer.style.display = "block";
},
//保存手动产生的数据行数据
save:function(){
if(!this.__editRow){
return;
}
var row = this.__editRow;
var td = row.firstChild;
var data = {};
while(td){
if(td.nodeType === 1 && td.field){
var dataFormat = this.__cache["dataFormat"][td.field];
var textarea = null;
if(dataFormat.render == "innerHTML"){
textarea = td.getElementsByTagName("textarea")[0];
}else{
textarea = td.getElementsByTagName("input")[0];
}
value = textarea.value;
switch(dataFormat.dataType){
case "number":
value = util.trim(value);
value = Number(value.length == 0 ? 0 : value);
break;
default:
break;
}
data[td.field] = value;
}
td = td.nextSibling;
}
this.__editRow.parentNode.removeChild(this.__editRow);
this.__editRow = null;
this.__saveContainer.style.display = "none";
//通知用户正在保存数据
if(util.isFunction(this.onSaving)){
this.onSaving(data);
}
this.append(data);
},
getRowData:function(rowIndex){
return this.__cache["customCache"].get(rowIndex);
},
//数据绑定到指定cell时的事件
onDataBinding:function(field,value,cell,data){
return value;
},
//当数据行绑定开始时的事件
onRowBinding:function(rowIndex, row){
},
//当数据行绑定完成时的事件
//@param row {DOM element tr}
onRowBinded:function(rowIndex, row){
},
//当编辑的数据被保存时的事件
onSaving:function(data){
},
//当数据行被移除时的通知事件
onRowRemoved:function(data,row){
}
};
grid.guid = 0;
})();
</script>
</head>
<body>
<table id="table_demo" class="grid">
<tr class="odd">
<th>ID</th>
<th>Name</th>
<th>Descpription</th>
<th><button type="button" command="newLine" class="btn">New Line</button></th>
</tr>
<tr>
<td data='{"field":"id","dataType":"number","render":"innerText"}'>1</td>
<td data='{"field":"name","dataType":"string","render":"innerText"}'>WorkingService</td>
<td data='{"field":"description","dataType":"string","render":"innerHTML"}'>WorkingService</td>
<td>
<button type="button" command="removeRow" class="btn">Delete</button>
</td>
</tr>
</table>
<script type="text/javascript">
var table = document.getElementById("table_demo");
var g = new grid(table,{
onDataBinding:function(field,value){
return value;
},
onRowBinded:function(rowIndex,row){}
});
g.bind([
{id:0,name:"kilin"},
{id:1,name:"kilin1"},
{id:2,name:"kilin2"},
{id:3,name:"kilin3"}
]);
</script>
</body>
</html>
登录后复制