用javascript实现的TreeTable, 可以当做树(Tree)用

来源:互联网 发布:php 模板引擎 编辑:程序博客网 时间:2024/06/03 15:58
 这个TreeTable,如果只有一列的话,就是常用的Tree控件了

这是我写的TreeTable.js文件
Javascript代码 复制代码 收藏代码
  1. var _treeTableIcons = {};   
  2. // 树结点是否有竖线,因为如果有竖线的话,行高过高,线就不能连在一起,很难看,最好像windows的资源管理器一样,不要添加结点之间的连线。默认也是不添加连线的  
  3. var showLine = false;   
  4. if (showLine) {   
  5.     _treeTableIcons['empty'] = 'images/empty.gif';   
  6.     _treeTableIcons['folder'] = 'images/folder.gif';   
  7.     _treeTableIcons['folderopen'] = 'images/folderopen.gif';   
  8.     _treeTableIcons['join'] = 'images/join.gif';   
  9.     _treeTableIcons['joinbottom'] = 'images/joinbottom.gif';   
  10.     _treeTableIcons['line'] = 'images/line.gif';   
  11.     _treeTableIcons['minus'] = 'images/minus.gif';   
  12.     _treeTableIcons['minusbottom'] = 'images/minusbottom.gif';   
  13.     _treeTableIcons['nolines_minus'] = 'images/nolines_minus.gif';   
  14.     _treeTableIcons['nolines_plus'] = 'images/nolines_plus.gif';   
  15.     _treeTableIcons['page'] = 'images/page.gif';   
  16.     _treeTableIcons['plus'] = 'images/plus.gif';   
  17.     _treeTableIcons['plusbottom'] = 'images/plusbottom.gif';   
  18. else {   
  19.     _treeTableIcons['empty'] = 'images/empty.gif';   
  20.     _treeTableIcons['folder'] = 'images/folder.gif';   
  21.     _treeTableIcons['folderopen'] = 'images/folderopen.gif';   
  22.     _treeTableIcons['join'] = 'images/empty.gif';   
  23.     _treeTableIcons['joinbottom'] = 'images/empty.gif';   
  24.     _treeTableIcons['line'] = 'images/empty.gif';   
  25.     _treeTableIcons['minus'] = 'images/nolines_minus.gif';   
  26.     _treeTableIcons['minusbottom'] = 'images/nolines_minus.gif';   
  27.     _treeTableIcons['nolines_minus'] = 'images/nolines_minus.gif';   
  28.     _treeTableIcons['nolines_plus'] = 'images/nolines_plus.gif';   
  29.     _treeTableIcons['page'] = 'images/page.gif';   
  30.     _treeTableIcons['plus'] = 'images/nolines_plus.gif';   
  31.     _treeTableIcons['plusbottom'] = 'images/nolines_plus.gif';   
  32. }   
  33.   
  34. function TreeTable(layout, model, divId, id) {   
  35.     this.divId = divId;   
  36.     this.mapping = {};   
  37.     this.model = model;   
  38.     this.layout = layout;   
  39.     this.addNode = _addNode;   
  40.     this.startup = _startup;   
  41.     this.setRoot = _setRoot;   
  42.     this.getRoot = _getRoot;   
  43.     this.expandNode = _expandNode;   
  44.     this.expandAll = _expandAll;   
  45.     TreeTable.prototype.instants[this.id = id ? id : TreeTable.prototype._treeIdPrefix + TreeTable.prototype.index++] = this;   
  46. }   
  47.   
  48. function TreeNode(item) {   
  49.     this.item = item;   
  50.     this.nodes = [];   
  51. }   
  52.   
  53. TreeTable.prototype._treeIdPrefix = 'treeTable_';   
  54. TreeTable.prototype.instants = {};   
  55. TreeTable.prototype.index = 0;   
  56. TreeNode.prototype.index = 0;   
  57.   
  58. function _addNode(parentNode, childNode) {   
  59.     if (parentNode) {   
  60.         childNode.parentId = parentNode.id;   
  61.         childNode.id = parentNode.id + '_' + TreeNode.prototype.index++;   
  62.         parentNode.nodes[parentNode.nodes.length] = childNode;   
  63.         childNode.parent = parentNode;   
  64.     } else {   
  65.         childNode.id = this.id + '_' + TreeNode.prototype.index++;   
  66.     }   
  67.     childNode.isOpened = true;   
  68.     this.mapping[childNode.id]=childNode;   
  69. }   
  70.   
  71. function _getRoot() {   
  72.     return this.rootNode;   
  73. }   
  74.   
  75. function _setRoot(rootNode) {   
  76.     this.addNode(null, rootNode);   
  77.     this.rootNode = rootNode;   
  78. }   
  79.   
  80. function _startup() {   
  81.     if (this.layout && this.layout.constructor == Array && this.layout.length > 0) {   
  82.         _makeupNodes(this);   
  83.         var tableHeaderStr = '<thead class="treeTableHeader"><tr>';   
  84.         for (var i = 0; i < layout.length; i++) {   
  85.             var headerClass = this.layout[i].headerClass ? ' class="' + this.layout[i].headerClass + '"' : '';   
  86.             tableHeaderStr += '<td' + headerClass + '>' + this.layout[i].name + '</td>';   
  87.         }   
  88.         tableHeaderStr += '</tr></thead>';   
  89.         var tableStr = '<table id="' + this.id + '" class="treeTable">' + tableHeaderStr + _makeupHTML(thisthis.layout, this.getRoot(), this.getRoot(), -1, -1, '') + '</table>';   
  90.         document.getElementById(this.divId).innerHTML = tableStr;   
  91.     }   
  92. }   
  93.   
  94. function _makeupNodes(treeTable) {   
  95.     var model = treeTable.model;   
  96.     var jsonStore = model.store;   
  97.     if (jsonStore) {   
  98.         var childrenAttrs = model.childrenAttrs;   
  99.         _traverseModel(treeTable, null, jsonStore, childrenAttrs);   
  100.     }   
  101. }   
  102.   
  103. function _traverseModel(treeTable, parentNode, item, childrenAttrs) {   
  104.     if (item) {   
  105.         var treeNode = new TreeNode(item);   
  106.         treeTable.addNode(parentNode, treeNode);   
  107.         var children = item[childrenAttrs];   
  108.         if (children && children.constructor == Array) {   
  109.             for ( var i = 0; i < children.length; i++) {   
  110.                 _traverseModel(treeTable, treeNode, children[i], childrenAttrs);   
  111.             }   
  112.         }   
  113.         if (!parentNode) {   
  114.             treeTable.setRoot(treeNode);   
  115.         }   
  116.     }   
  117. }   
  118.   
  119. function _makeupHTML(treeTable, layout, rootNode, treeNode, index, count, indent) {   
  120.     var htmlStr = '';   
  121.     if (treeNode && treeNode.item) {   
  122.         var isFolderNode = (treeNode.nodes.length > 0);   
  123.         htmlStr = '<tr id="' + treeNode.id + '" class="treeTableRow">\n';   
  124.         for (var colIdx = 0; colIdx < layout.length; colIdx++) {   
  125.             var tdText = layout[colIdx].get ? layout[colIdx].get(treeNode.item, colIdx, treeNode, treeTable) : _get(treeNode.item, colIdx, treeNode, treeTable);   
  126.             var className = layout[colIdx].className;   
  127.             var style = layout[colIdx].style;   
  128.             var icon = layout[colIdx].getIcon ? layout[colIdx].getIcon(treeNode.item, colIdx, treeNode, true, treeTable) : _getIcon(treeNode.item, colIdx, treeNode, true, treeTable);   
  129.             var iconElement = '';   
  130.             if (icon && icon != '') {   
  131.                 iconElement = '<img src="' + icon + '" style="vertical-align: middle" alt="" />';   
  132.             }   
  133.             if (colIdx == 0){   
  134.                 var imageStr = '';   
  135.                 var eventStr = '';   
  136.                 if (isFolderNode) {   
  137.                     eventStr = isFolderNode ? ' id="folder_' + treeNode.id + '" onclick="javascript:handleNodeClick(event)"' : '';   
  138.                 }   
  139.                 if (index == count - 1) {   
  140.                     imageStr = _treeTableIcons[isFolderNode ? 'minusbottom' : 'joinbottom'];   
  141.                 } else {   
  142.                     imageStr = _treeTableIcons[isFolderNode ? (treeNode == rootNode ? 'nolines_minus' : 'minus') : 'join'];   
  143.                 }   
  144.                 tdText = indent + '<img src="' + imageStr + '" style="vertical-align: middle" alt="" ' + eventStr + ' />' + iconElement + tdText;   
  145.             } else {   
  146.                 tdText = iconElement + tdText;   
  147.             }   
  148.             htmlStr += '  <td class="treeTableCell' + (className ? ' ' + className : '') + '" ' + (style ? 'style="' + style + '" ' : '') + '>' + tdText + '</td>\n';   
  149.         }   
  150.         htmlStr += '</tr>\n';   
  151.         for (var i = 0; i < treeNode.nodes.length; i++) {   
  152.             var nextIndent = indent + ((index != -1 && index != count - 1) ? '<img src="' + _treeTableIcons['line'] + '" style="vertical-align: middle" alt="" />' : '<img src="' + _treeTableIcons['empty'] + '" style="vertical-align: middle" alt="" />');   
  153.             htmlStr += _makeupHTML(treeTable, layout, rootNode, treeNode.nodes[i], i, treeNode.nodes.length, nextIndent);   
  154.         }   
  155.     }   
  156.     return htmlStr;   
  157. }   
  158.   
  159. function _get(item, column, treeNode, treeTable) {   
  160.     var layout = treeTable.layout;   
  161.     return treeNode.item[layout[column].field];   
  162. }   
  163.   
  164. function _getIcon(item, column, treeNode, isOpened, tableTree) {   
  165.     if (column == 0) {   
  166.         if (treeNode.nodes.length > 0) {   
  167.             return _treeTableIcons[isOpened ? 'folderopen' : 'folder'];   
  168.         } else {   
  169.             return _treeTableIcons['page'];   
  170.         }   
  171.     }   
  172.     return '';   
  173. }   
  174.   
  175. function _expandNode(treeNode, isOpened) {   
  176.     _expand(this, treeNode, isOpened, false);   
  177. }   
  178.   
  179. function _expandAll(isOpened) {   
  180.     var rootNode = this.getRoot();   
  181.     if (rootNode) {   
  182.         _expand(this, rootNode, isOpened, true);   
  183.     }   
  184. }   
  185.   
  186. function _expand(treeTable, treeNode, isOpened, isOpenAll) {   
  187.     if (!treeNode) {   
  188.         return;   
  189.     }   
  190.   
  191.     var subTreeNodes = treeNode.nodes;   
  192.     if (subTreeNodes && subTreeNodes.length > 0) {   
  193.         var source = document.getElementById('folder_' + treeNode.id);   
  194.         var trNode = source.parentNode.parentNode;   
  195.         var folderNode = source.nextSibling;   
  196.         var itemId = trNode.id;   
  197.         var parentNode = source.parentNode;   
  198.         while (parentNode.tagName.toLowerCase() != 'table') {   
  199.             parentNode = parentNode.parentNode;   
  200.         }   
  201.            
  202.         folderNode.setAttribute('src', _treeTableIcons[isOpened ? 'folderopen' : 'folder']);   
  203.         var isRootNode = treeNode == treeTable.getRoot();   
  204.         if (isRootNode) {   
  205.             source.setAttribute('src', _treeTableIcons[isOpened ? 'nolines_minus' : 'nolines_plus']);   
  206.         } else {   
  207.             var isLastTreeNode = treeNode.parent.nodes[treeNode.parent.nodes.length - 1] == treeNode ? true : false;   
  208.             if (isLastTreeNode) {   
  209.                 source.setAttribute('src', _treeTableIcons[isOpened ? 'minusbottom' : 'plusbottom']);   
  210.             } else {   
  211.                 source.setAttribute('src', _treeTableIcons[isOpened ? 'minus' : 'plus']);   
  212.             }   
  213.         }   
  214.   
  215.         for (var i = 0; i < subTreeNodes.length; i++) {   
  216.             var subTreeNode = subTreeNodes[i];   
  217.             var subTrNode = document.getElementById(subTreeNode.id);   
  218.             if (subTrNode) {   
  219.                 subTrNode.style.display = isOpened ? 'table-row' : 'none';   
  220.             }   
  221.             if (!isOpenAll && isOpened && !subTreeNode.isOpened) {   
  222.                 continue;   
  223.             }   
  224.             if (isOpenAll) {   
  225.                 treeNode.isOpened = isOpened;   
  226.             }   
  227.             _expand(treeTable, subTreeNode, isOpened, isOpenAll);   
  228.         }   
  229.     }   
  230. }   
  231.   
  232. function handleNodeClick(event) {   
  233.     var source = event.currentTarget || event.srcElement;   
  234.     var trNode = source.parentNode.parentNode;   
  235.     var itemId = trNode.id;   
  236.     var parentNode = source.parentNode;   
  237.     while (parentNode.tagName.toLowerCase() != 'table') {   
  238.         parentNode = parentNode.parentNode;   
  239.     }   
  240.     var treeTable = TreeTable.prototype.instants[parentNode.id];   
  241.     var treeNode = treeTable.mapping[itemId];   
  242.     var isOpened = treeNode.isOpened;   
  243.     treeTable.expandNode(treeNode, !isOpened);   
  244.     treeNode.isOpened = !isOpened;   
  245. }  


默认TableTree.css文件
Css代码 复制代码 收藏代码
  1. .treeTable {   
  2.     width: 100%;   
  3.     border-collapse: collapse;   
  4.     border: solid 1px #e8e8e8;   
  5. }   
  6.   
  7. .treeTableHeader {   
  8. }   
  9.   
  10. .treeTableHeader td {   
  11.     height: 20px;   
  12.     line-height: 20px;   
  13.     font-size: 12px;   
  14.     font-weight: bold;   
  15.     text-align: center;   
  16. }   
  17.   
  18. .treeTableRow {   
  19.     margin: 0;   
  20.     padding: 0;   
  21. }   
  22.   
  23. .treeTableCell {   
  24.     height: 25px;   
  25.     line-height: 25px;   
  26.     font-size: 11px;   
  27.     font-weight: bold;   
  28.     margin: 0;   
  29.     padding: 0;   
  30.     border: solid 1px #e8e8e8;   
  31. }  


------------------------------------------------------
以下是此TreeTable的应用实例:
首先,我们需要一个JSON文件来做模型,这个JSON文件需要是很规范的,而且有层级关系
JSON文件(TreeTableTest.json)的内容如下:
Json代码 复制代码 收藏代码
  1. {   
  2.     id: 'root',   
  3.     name: 'root_node',   
  4.     value: 'root_value',   
  5.     children: [   
  6.         {   
  7.             id: 'node_0',   
  8.             name: 'node_0_name',   
  9.             value: 'node_0_value',   
  10.             children: [   
  11.                 {   
  12.                     id: 'node_0_0',   
  13.                     name: 'node_0_0_name',   
  14.                     value: 'node_0_0_value',   
  15.                     children: [   
  16.                         {   
  17.                             id: 'node_0_0',   
  18.                             name: 'node_0_0_name',   
  19.                             value: 'node_0_0_value',   
  20.                             children: [   
  21.                                 {   
  22.                                     id: 'node_0_0_0',   
  23.                                     name: 'node_0_0_0_name',   
  24.                                     value: 'node_0_0_0_value',   
  25.                                 },   
  26.                                 {   
  27.                                     id: 'node_0_0_1',   
  28.                                     name: 'node_0_0_1_name',   
  29.                                     value: 'node_0_0_1_value',   
  30.                                 }   
  31.                             ]   
  32.                         },   
  33.                         {   
  34.                             id: 'node_0_1',   
  35.                             name: 'node_0_1_name',   
  36.                             value: 'node_0_1_value',   
  37.                             children: [   
  38.                                 {   
  39.                                     id: 'node_0_1_0',   
  40.                                     name: 'node_0_1_0_name',   
  41.                                     value: 'node_0_1_0_value',   
  42.                                 },   
  43.                                 {   
  44.                                     id: 'node_0_1_1',   
  45.                                     name: 'node_0_1_1_name',   
  46.                                     value: 'node_0_1_1_value',   
  47.                                 }   
  48.                             ]   
  49.                         }   
  50.                     ]   
  51.                 }   
  52.             ]   
  53.         },   
  54.         {   
  55.             id: 'node_1',   
  56.             name: 'node_1_name',   
  57.             value: 'node_1_value',   
  58.             children: [   
  59.                 {   
  60.                     id: 'node_1_0',   
  61.                     name: 'node_1_0_name',   
  62.                     value: 'node_1_0_value',   
  63.                     children: [   
  64.                         {   
  65.                             id: 'node_1_0',   
  66.                             name: 'node_1_0_name',   
  67.                             value: 'node_1_0_value',   
  68.                             children: [   
  69.                                 {   
  70.                                     id: 'node_1_0_0',   
  71.                                     name: 'node_1_0_0_name',   
  72.                                     value: 'node_1_0_0_value',   
  73.                                 },   
  74.                                 {   
  75.                                     id: 'node_1_0_1',   
  76.                                     name: 'node_1_0_1_name',   
  77.                                     value: 'node_1_0_1_value',   
  78.                                 }   
  79.                             ]   
  80.                         },   
  81.                         {   
  82.                             id: 'node_1_1',   
  83.                             name: 'node_1_1_name',   
  84.                             value: 'node_1_1_value',   
  85.                             children: [   
  86.                                 {   
  87.                                     id: 'node_1_1_0',   
  88.                                     name: 'node_1_1_0_name',   
  89.                                     value: 'node_1_1_0_value',   
  90.                                 },   
  91.                                 {   
  92.                                     id: 'node_1_1_1',   
  93.                                     name: 'node_1_1_1_name',   
  94.                                     value: 'node_1_1_1_value',   
  95.                                 }   
  96.                             ]   
  97.                         }   
  98.                     ]   
  99.                 }   
  100.             ]   
  101.         }   
  102.     ]   
  103. }  


测试用的HTML文件(TreeTableTest.html)的代码如下:
Html代码 复制代码 收藏代码
  1. <html>  
  2. <head>  
  3.     <title>Tree Table Test</title>  
  4.     <link rel="stylesheet" type="text/css" href="styles/TreeTable.css" />  
  5.     <link rel="stylesheet" type="text/css" href="styles/TreeTableTest.css" />  
  6.     <script type="text/javascript" src="js/TreeTable.js"></script>  
  7.     <script type="text/javascript" src="js/jquery.min.js"></script>  
  8.     <script type="text/javascript" src="js/TreeTableTest.js"></script>  
  9. </head>  
  10. <body>  
  11.     <div id='test_table'></div>  
  12. </body>  
  13. </html>  


因为测试用的代码不想把json对象hardcode到js文件中,所以使用了ajax去读取json对象,引入了jquery的代码,也可以用其它的ajax框架,或直接用xmlRequest对象。

测试用的JS文件(TreeTableTest.js)代码:
Javascript代码 复制代码 收藏代码
  1. $(document).ready(function () {   
  2.     $.post('json/TreeTableTest.json?date=' + new Date().valueOf(), {type:"slides"}, function (data) {   
  3.         initTreeTable(data);   
  4.     });   
  5. })   
  6.   
  7. function initTreeTable(data) {   
  8.     json = eval('(' + data + ')');   
  9.     model = {   
  10.         store: json,   
  11.         childrenAttrs: ['children']   
  12.     };   
  13.     layout = [   
  14.         {name: 'Id', field: 'id', headerClass: 'id_col', className: 'id_col', style: 'width: 20%'},   
  15.         {name: 'Name', field: 'name', headerClass: 'name_col', className: 'name_col', style: 'width: 60%', get: testGetName, getIcon: testGetIcon},   
  16.         {name: 'Value', field: 'value', headerClass: 'value_col', className: 'value_col', style: 'width: 20%', get: testGetValue}   
  17.     ];   
  18.     var treeTable = new TreeTable(layout, model, 'test_table');   
  19.     treeTable.startup();   
  20. //  treeTable.expandAll(false);   
  21. }   
  22.   
  23. function testGetName(item) {   
  24.     return '<a href="javascript:;">' + item.id + '</a>';   
  25. }   
  26.   
  27. function testGetValue(item, column, treeNode) {   
  28.     if (treeNode.nodes.length == 0) {   
  29.         return '<input type="text" value="' + item.value + '"/>';   
  30.     }   
  31.     return '';   
  32. }   
  33.   
  34. function testGetIcon(item) {   
  35.     return 'images/page.gif';   
  36. }  


目前TreeTable支持每个单元格的css, style和icon的定制,且方法极其简单。对于列和单元格中的数据展示都可以定制,很灵活。

需要的css文件(TestTreeTable.css)代码如下:
Css代码 复制代码 收藏代码
  1. .id_col {   
  2.     background: yellow;   
  3. }   
  4.   
  5. .name_col {   
  6.     background: gray;   
  7. }   
  8.   
  9. .value_col {   
  10.     border: green;   
  11. }   
  12.   
  13. .value_col input {   
  14.     margin: 0;   
  15.     padding: 0;   
  16.     height: 16px;   
  17.     width: 100%;   
  18. }  


以后装要完成的需求:
1.基于Javascript实现
2.支持通过程序控制的方式来实现动态的修改节点
3.支持展开,收缩等等标准操作
4.支持异步加载数据的方式
5.在编程控制方面,完成基本的设置(Javascript文件的引用或者css中behavior的声明)之后就可以基于对象的控制

扩展性
6.支持拖动
7.支持自定义右键菜单
7.在frame布局中支持target方式的自动链接
8.支持tooltip