/** * * VrWorking Data Grid Class v0.3a * 渲染 JSON 数据到表格 * 可指定浮动标题行 * 可指定是否显示序号列 * 2010-3-12 * 2010-3-15 完成统一样式定义,类里面定义样式,可以外部修改;分段载入数据;浮动标题行完全实现,并具备兼容 FF 的特性(1268585210890) * 2010-4-12 更正没有初始化 current 索引的错误,因为没有遇到 this.generateSectionHtml(false) 执行的条件,考虑删除 this.generateSectionHtml() 方法中 next 条件为 false 的情况处理 * 2010-4-20 修正当前行(current)数值错误导致输出重复的行 * * * * * * * 需要解决的: * 1. 浮动标题列,指定几个列可以在横向滚动时浮动 * [OK] 2. 完全动态获取一些 CSS 定义的值,可以适应外部 CSS 定义 * 3. 重新规划方法的划分和命名,以及运行的方式 * [OK] 4. 动态分段载入数据行 * 5. 逆序显示数据时序号反转,就是从 1 开始,或者指定序号开始值、步长 * 6. 定义列对齐位置(left center right) * [OK] 7. 使用外部样式定义,例如: 通过在 JSON 中定义属性,来加载不同的样式 * 8. 分页显示 * 9. 表头点击排序 * 10. 控制列,以及相关事件的绑定,例如增加“编辑”“删除”等控制列,或者每行绑定不同的右键菜单 * */ var VrWorking = {}; VrWorking.prototype = VrWorking; VrWorking.prototype.hasProperty = function (property, o) { /// 检查给定对象是否包含相关属性 if (o == null) return false; if (property == '') return false; for (var p in o) if (p.toLowerCase() == property.toLowerCase()) return true; return false; }; VrWorking.prototype.position = function(e) { var pos = { 'left' : 0, 'top' : 0 }; if (!e) return pos; while(e && /^body|html$/i.test(e.tagName) == false) { pos['left'] = pos['left'] + e.offsetLeft; pos['top'] = pos['top'] + e.offsetTop; try { e = e.offsetParent; } catch (v) { break; }; }; return pos; }; /** * * VrWorking.DataGrid * */ VrWorking.DataGrid = { 'author' : 'guanxuejun@126.com', 'version' : 0.3, 'update' : '2010-4-12', '_parent' : VrWorking, 'id' : new Date().valueOf(), 'group' : true, // 是否分组输出,就是多次加载,每次只加载一部分数据,依赖于 groupSize 属性 'groupSize' : 10, // 分组大小,如果设置分组输出,则定义每次输出的数据行数 'current' : 0, // 分组输出时标记当前数据加载的行序号 'appending' : false, // 分组输出时标记当前是否正在数据加载,防止多次触发 'searial' : true, // 数据序号,如果增加则浮动列包含此列 'floatRow' : true, // 浮动标题行 'floatCol' : false, // 浮动标题列,如果 searial 属性为 true ,则浮动 searial 列 'order' : '', // 输出顺序,如果得到数据顺序方向不正确,可以逆转顺序方向,但不是重新排序,默认为顺序输出 ASC 'cache' : true, // 是否缓存数据,如果是,则需要指定 interval 属性 'interval' : 10000, // 缓存数据更新间隔,单位是毫秒,当前时间减去 time 属性记录的时间,如果大于此间隔,则需要重新获取数据 'time' : new Date().valueOf(), // 拿到数据的时间,该值是 1970 年 1 月 1 日午夜到当前 UTC 时间所走过的毫秒数 'ui' : { 'target' : { 'width' : '200px', 'height' : '200px', 'padding' : '1px', 'margin' : '10px', 'overflow' : 'auto', 'border' : '0px' }, 'dataTable' : { 'border' : '1px solid #8bad76', 'borderCollapse' : 'collapse' }, 'dataTableTh' : { 'border' : '1px solid #8bad76', 'borderCollapse' : 'collapse', 'backgroundColor' : '#fffdd2', 'wordBreak' : 'keep-all', 'padding' : '4px' }, 'dataTableTd' : { 'border' : '1px solid #8bad76', 'borderCollapse' : 'collapse', 'padding' : '4px' }, 'floatDiv' : { 'position' : 'absolute', 'top' : '0px', 'left' : '0px', 'width' : '10px', 'margin' : '0px', 'padding' : '1px', 'zIndex' : 100, 'overflow' : 'hidden' }, 'floatTable' : { 'position' : 'relative', 'top' : '0px', 'left' : '0px', 'border' : '1px solid #8bad76', 'borderCollapse' : 'collapse' }, 'floatTableTh' : { 'border' : '1px solid #8bad76', 'borderCollapse' : 'collapse', 'backgroundColor' : '#cceeee', 'wordBreak' : 'keep-all', 'padding' : '4px' } } }; VrWorking.DataGrid.prototype = VrWorking.DataGrid; VrWorking.DataGrid.prototype.clone = function (){ var clonePrototype = function () {}; clonePrototype.prototype = this; var obj = new clonePrototype(); for(var ele in obj){ if(typeof(obj[ele]) == "object") { obj[ele] = obj[ele].clone(); }; }; return obj; }; VrWorking.DataGrid.prototype.load = function (json) { /// 数据表初始化 if (json == null) return this; /// 检查数据对象合法性 if (!this._parent.hasProperty('dataHead', json)) { alert('缺少属性: dataHead'); return this; }; if (!this._parent.hasProperty('dataBody', json)) { alert('缺少属性: dataBody'); return this; }; this['id'] = new Date().valueOf(); this['time'] = new Date().valueOf(); this['rows'] = json['dataBody'].length; this['cols'] = (function (db) { var maxCols = 0; for (var i=0; i<db.length; i++) for (var j=0; j<db[i].length; j++) maxCols = (j > maxCols ? j : maxCols); return maxCols+1; })(json['dataBody']); this['order'] = (this['order'] == '' ? 'ASC' : 'DESC'); this['interval']= 10000; var offset = this['cols']-json['dataHead'].length; if (offset > 0) for (var i=0; i<offset; i++) json['dataHead'].push('[H]'); this['data'] = json; /// 缓存数据到属性 if (this['group']) { if (this['rows'] <= this['groupSize']) { this.generateHtml(); } else { this.generateSectionHtml(); }; } else { this.generateHtml(); }; return this; }; VrWorking.DataGrid.prototype.generateSectionHtml = function (next) { /// 生成分组数据 到 HTML 代码 var size = this['groupSize']; var rows = this['rows']; var cols = this['cols']; var head = this['data']['dataHead']; var body = this['data']['dataBody']; if (next == true) { if (/^[0-9]+$/.test(this['current']) == false) { alert('错误:加载后续数据错误!'); return; }; if (this['current'] < 0 || this['current'] > rows) return; var start = this['current']; var code = ''; if (this['order'] == 'ASC') { for (var i=start; i<(start+size); i++) { if (this['current'] == rows) break; code += '<tr>'; if (this['searial']) code += '<td nowrap="nowrap">'+(this['current']+1).toString()+'</td>'; for (var j=0; j<cols; j++) code += '<td nowrap="nowrap">'+(body[i][j] === undefined ? '[E]' : body[i][j])+'</td>'; code += '</tr>'; this['current'] = this['current'] + 1; }; } else { for (var i=start; i>=(start-size+1); i--) { if (this['current'] < 0) break; code += '<tr>'; if (this['searial']) code += '<td nowrap="nowrap">'+(this['current']+1).toString()+'</td>'; for (var j=0; j<cols; j++) code += '<td nowrap="nowrap">'+(body[i][j] === undefined ? '[E]' : body[i][j])+'</td>'; code += '</tr>'; this['current'] = this['current'] - 1; }; }; this['html'] = code.replace(/[/n|/r]+/gi, ''); /// 缓存数据到属性 } else { var code = '<table id="table_'+this['id']+'" cellpadding="0" cellspacing="0" border="0">'; code += '<thead><tr>'; if (this['searial']) code += '<th nowrap="nowrap">序号</th>'; for (var i=0; i<head.length; i++) code += '<th nowrap="nowrap">'+head[i]+'</th>'; code += '</tr></thead>'; code += '<tbody>'; if (this['order'] == 'ASC') { this['current'] = 0; for (var i=0; i<size; i++) { code += '<tr>'; if (this['searial']) code += '<td nowrap="nowrap">'+(this['current']+1).toString()+'</td>'; for (var j=0; j<cols; j++) code += '<td nowrap="nowrap">'+(body[i][j] === undefined ? '[E]' : body[i][j])+'</td>'; code += '</tr>'; this['current'] = this['current'] + 1; }; } else { this['current'] = rows - 1; for (var i=rows-1; i>=(rows-size+1); i--) { code += '<tr>'; if (this['searial']) code += '<td nowrap="nowrap">'+(this['current']+1).toString()+'</td>'; for (var j=0; j<cols; j++) code += '<td nowrap="nowrap">'+(body[i][j] === undefined ? '[E]' : body[i][j])+'</td>'; code += '</tr>'; this['current'] = this['current'] - 1; }; }; code += '</tbody></table>'; this['html'] = code.replace(/[/n|/r]+/gi, ''); /// 缓存数据到属性 }; }; VrWorking.DataGrid.prototype.generateHtml = function () { /// 生成数据表 HTML 代码字串 var rows = this['rows']; var cols = this['cols']; var head = this['data']['dataHead']; var body = this['data']['dataBody']; var code = '<table id="table_'+this['id']+'" cellpadding="0" cellspacing="0" border="0">'; code += '<thead><tr>'; if (this['searial']) code += '<th nowrap="nowrap">序号</th>'; for (var i=0; i<head.length; i++) code += '<th nowrap="nowrap">'+head[i]+'</th>'; code += '</tr></thead>'; code += '<tbody>'; if (this['order'] == 'ASC') { this['current'] = 0; for (var i=0; i<rows; i++) { code += '<tr>'; if (this['searial']) code += '<td nowrap="nowrap">'+(i+1).toString()+'</td>'; for (var j=0; j<cols; j++) code += '<td nowrap="nowrap">'+(body[i][j] === undefined ? '[E]' : body[i][j])+'</td>'; code += '</tr>'; this['current'] = this['current'] + 1; }; } else { this['current'] = rows - 1; for (var i=rows-1; i>=0; i--) { code += '<tr>'; if (this['searial']) code += '<td nowrap="nowrap">'+(i+1).toString()+'</td>'; for (var j=0; j<cols; j++) code += '<td nowrap="nowrap">'+(body[i][j] === undefined ? '[E]' : body[i][j])+'</td>'; code += '</tr>'; this['current'] = this['current'] + 1; }; }; code += '</tbody></table>'; this['html'] = code.replace(/[/n|/r]+/gi, ''); /// 缓存数据到属性 }; VrWorking.DataGrid.prototype.append = function () { if (!this['group']) return; if (this['current'] > this['rows']) return; this.generateSectionHtml(true); var code = this['target'].innerHTML.replace(/[/n|/r]+/g, ''); code = code.replace(/(<//tbody><//table><div){1}/i, this['html'].toString()+'</tbody></table><div'); this['target'].innerHTML = code; this['tableData'] = document.getElementById('table_'+this['id'].toString()); this.formatStyle(this['tableData'], this['ui']['dataTable']); this.formatStyle(this['tableData'].getElementsByTagName('th'), this['ui']['dataTableTh']); this.formatStyle(this['tableData'].getElementsByTagName('td'), this['ui']['dataTableTd']); this['floatDiv'] = document.getElementById('float_div_'+this['id'].toString()); this['floatHead'] = document.getElementById('float_head_'+this['id'].toString()); this['appending'] = false; }; VrWorking.DataGrid.prototype.render = function (target, ui) { /// 是否更改预置样式定义 if (ui != undefined) for (var item in this['ui']) if (ui.hasOwnProperty(item)) for (var d in ui[item]) this['ui'][item][d] = ui[item][d]; /// 渲染数据表到指定位置 if (target == '') return; if (document.getElementById(target) == null) return; this['target'] = document.getElementById(target); this.formatStyle(this['target'], this['ui']['target']); this['target'].innerHTML = this['html']; this['tableData'] = document.getElementById('table_'+this['id'].toString()); this.formatStyle(this['tableData'], this['ui']['dataTable']); this.formatStyle(this['tableData'].getElementsByTagName('th'), this['ui']['dataTableTh']); this.formatStyle(this['tableData'].getElementsByTagName('td'), this['ui']['dataTableTd']); if (!this['floatRow']) return; /// 创建浮动表头 this.fly(); /// 添加事件 var _self = this; this['target'].onscroll = function () { _self.fly(); if (_self['group']) { if (_self['current'] < _self['rows']) { /// 自动加载后续数据 var ht = parseInt(_self['target'].clientHeight); var sc = parseInt(_self['target'].scrollTop); var oh = parseInt(_self['tableData'].clientHeight); if ((ht + sc) >= oh) { if (_self['appending']) return; _self['appending'] = true; var _me = _self; window.setTimeout(function (){ _me.append(); }, 1000); }; }; }; }; }; VrWorking.DataGrid.prototype.formatStyle = function (e, ui) { if (!e || !ui) return; if (e.length == undefined) { for (var p in ui) e.style[p] = ui[p]; return; }; for (var i=0; i<e.length; i++) { for (var p in ui) e[i].style[p] = ui[p]; }; }; VrWorking.DataGrid.prototype.fly = function () { if (this['floatDiv']) { /// 已经存在 调整列宽 if (isNaN(this['target'].scrollLeft)) { this.formatStyle(this['floatDiv'], { 'top': (this._parent.position(this['target'])['top']).toString()+'px' }); } else { this.formatStyle(this['floatHead'], { 'left': (0-this['target'].scrollLeft).toString()+'px' }); }; var tho = this['tableData'].getElementsByTagName('th'); var thf = this['floatHead'].getElementsByTagName('th'); for (var i=0; i<thf.length; i++) thf[i].style['width'] = (tho[i].clientWidth-parseInt(tho[i].style['padding'])*2-parseInt(tho[i].style['borderWidth'])).toString()+'px'; return; }; /// 不存在,创建 var cols = this['cols']; var head = this['data']['dataHead']; var code = '<div id="float_div_'+this['id'].toString()+'"><table id="float_head_'+this['id'].toString()+'" align="left" cellpadding="0" cellspacing="0" border="0"><tr>'; if (this['searial']) code += '<th nowrap="nowrap">序号</th>'; for (var i=0; i<cols; i++) code += '<th nowrap="nowrap">'+head[i]+'</th>'; code += '</tr></table></div>'; this['target'].innerHTML = this['target'].innerHTML + code; this['tableData'] = document.getElementById('table_'+this['id'].toString()); this.formatStyle(this['tableData'], this['ui']['dataTable']); this.formatStyle(this['tableData'].getElementsByTagName('th'), this['ui']['dataTableTh']); this.formatStyle(this['tableData'].getElementsByTagName('td'), this['ui']['dataTableTd']); this['floatDiv'] = document.getElementById('float_div_'+this['id'].toString()); this.formatStyle(this['floatDiv'], this['ui']['floatDiv']); this['floatHead'] = document.getElementById('float_head_'+this['id'].toString()); this.formatStyle(this['floatHead'], this['ui']['floatTable']); this.formatStyle(this['floatHead'].getElementsByTagName('th'), this['ui']['floatTableTh']); this.formatStyle(this['floatDiv'], { 'top': (this._parent.position(this['target'])['top']).toString()+'px', 'left': (this._parent.position(this['target'])['left']).toString()+'px', 'width': (this['target'].clientWidth-parseInt(this['target'].style['padding'])-parseInt(this['target'].style['borderWidth'])).toString()+'px' }); var tho = this['tableData'].getElementsByTagName('th'); var thf = this['floatHead'].getElementsByTagName('th'); for (var i=0; i<thf.length; i++) { thf[i].style['width'] = (tho[i].clientWidth-parseInt(tho[i].style['padding'])*2-parseInt(tho[i].style['borderWidth'])).toString()+'px'; }; this['floatHead'].style['width'] = (document.all ? this['tableData'].clientWidth+2 : this['tableData'].clientWidth).toString()+'px'; }; VrWorking.DataGrid.prototype.reload = function () { /// 重新获取最新数据 }; VrWorking.DataGrid.prototype.refresh = function () { /// 从缓存刷新数据 }; window.onload = function () { var Matrix = { "title" : "70大中城市房屋价格指数", // 数据标题行,如果此数组长度小于 dataBody[0].length 则,自动补充列,文字为 "数据" "dataHead" : ["指数", "北京", "上海", "广州", "深圳"], // 数据体,一定是个严格的二维数组,对象的 rows, cols, dataHead.length 都以此为准重新赋值,数据体中的列如果不一致,则以最长的为准 "dataBody" : [ [ 13111.12, 3333.23, 533, 9001, 0.103 ], [ 3463.12, 5333.23, 533, 9001, 0.103 ], [ 13111.12, 2333.23, 533, 9001, 0.103 ], [ 1411.12, 333.23, 533, 9001, 0.103 ], [ 15111.12, 33.23, 533, 9001, 0.103 ], [ 16111.12, 3333.23, 533, 9001, 0.103 ], [ 17111.12, 3333.23, 533, 9001, 0.103 ], [ 18111.12, 3333.23, 533, 9001 ], [ 19111.12, 3333.23, 533, 9001 ], [ 20111.12, 3333.23, 533, 9001 ], [ 21111.12, 3333.23, 533, 9001 ], [ 22111.12, 2333.23, 533, 9001, 0.103 ], [ 23111.12, 333.23, 533, 9001, 0.103 ], [ 24111.12, 33.23, 533, 9001, 0.103 ], [ 25111.12, 3333.23, 533, 9001, 0.103 ], [ 26111.12, 3333.23, 533, 9001, 0.103 ], [ 27111.12, 3333.23, 533, 9001 ], [ 28111.12, 3333.23, 533, 9001 ], [ 29111.12, 3333.23, 533, 9001 ], [ 30111.12, 3333.23, 533, 9001 ], [ 31111.12, 3333.23, 533, 9001 ], [ 32111.12, 2333.23, 533, 9001, 0.103 ], [ 33111.12, 333.23, 533, 9001, 0.103 ], [ 34111.12, 33.23, 533, 9001, 0.103 ], [ 35111.12, 3333.23, 533, 9001, 0.103 ], [ 36111.12, 3333.23, 533, 9001, 0.103 ], [ 37111.12, 3333.23, 533, 9001 ], [ 38111.12, 3333.23, 533, 9001 ], [ 39111.12, 3333.23, 533, 9001 ], [ 40111.12, 3333.23, 533, 9001 ], [ 41111.12, 3333.23, 533, 9001 ], [ 42111.12, 3333.23, 533, 9001 ], [ 43111.12, 2333.23, 533, 9001, 0.103 ], [ 44111.12, 333.23, 533, 9001, 0.103 ], [ 45111.12, 33.23, 533, 9001, 0.103 ], [ 46111.12, 3333.23, 533, 9001, 0.103 ], [ 47111.12, 3333.23, 533, 9001, 0.103 ], [ 48111.12, 3333.23, 533, 9001 ], [ 49111.12, 3333.23, 533, 9001 ], [ 50111.12, 3333.23, 533, 9001 ], [ 51111.12, 3333.23, 533, 9001 ] ] }; var dataGrid = VrWorking.DataGrid; dataGrid.load(Matrix).render('DataGrid'); }; 这个类的本意是想取代总是要写的 List 页面,希望将所有的 List 页面集中在一个 PHP 页面显示,然后通过 AJAX 请求获取数据,展示在页面上。