<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>
0
0
相关文章
Next.js 13 中客户端组件间状态通信的正确实践
如何防止用户在 Select2 搜索框中输入特殊字符
动态调整 Streamlit 中文本区域(textarea)高度的完整实现方案
动态调整 Streamlit 中文本域(textarea)高度的完整实现方案
如何在 Streamlit 中实现文本域(text_area)的动态高度自适应
java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门AI工具
相关专题
本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。
463
2026.02.13
本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。
135
2026.02.13
本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。
64
2026.02.13
本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。
20
2026.02.13
本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。
26
2026.02.13
本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。
14
2026.02.12
本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。
524
2026.02.12
本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。
53
2026.02.12
热门下载
相关下载
精品课程
最新文章

