项目中用到不少的树和表格,但是要用到表格树的地方不是很多,但是有时候需要的时候却不好找到。所以就在闲暇的时候自己找了一下,结果发现了很简单的,但是不满足项目的需求,于是就看懂了别人的东西之后,再结合自己的理解,干脆就做一个表格树好了。从写下的第一句代码开始,没有想到居然写到了23k大小,而且发现还有很多待完善的地方。编写过程中遇到了一些麻烦问题,后面还好慢慢的解决了,现在对于这些问题还记忆犹新,所以就记录在此,大家有兴趣的看了可以提出意见,感激不尽。
需要准备的知识
代码使用了jquery框架,其余就是普通的javascript。具体的用到了javascript的内容有:动态建立表格,dom元素的基本操作,js的面向对象编程,json的数据等。虽然是jquery插件,实际代码中用到的jquery语句尽量的少,因为感觉使用jquery的话肯定是有一些性能的影响,大概jquery的使用的方法只占了10%到15%左右。对于浏览器兼容的问题,现在没有考虑很周到,所以在火狐下面不是很好用,我是用的ie6,不知道ie高版本会不会出现问题。
GridTree的基本结构
总体就是一个普通的table,程序是通过读取json数据(实际是js数组)里面的字段,然后通过动态添加表格的方法逐行添加tr,td,在添加行的过程中根据需要设置样式,添加方法等。每一行数据(即tr的dom对象)都有id属性,其值就等于数据中的idColumn对应的列的值,然后加上前缀‘_node’得到。例如加入有一行数据为'id:1,name:renjie120,parentId:-1',那么在形成的tr中要找到这一行的dom对象的话,通过document.getElementByIdx('_node1')就可以找到了。
GridTree树形数据层次结构的形成过程
json数据中的每行数据都提供了一个主标示列和父亲列,在配置中我取名叫做‘idColumn’和‘parentColumn’,也不知道合适不。然后表格树的重要一步就是根据这两列形成树形的结构,即把父亲和孩子们进行组织,然后再组装到表格中去。
我实现的方法是使用了一个js的hashMap对象(在hashMap.js对象中定义),然后利用hashMap,array就开始了组织树形的过程。主要相关代码如下:
// 父亲到孩子映射关系(key:父亲id,value:孩子id的集合)
var parentToChildMap= new HashMap();
// 孩子到父亲的映射关系(key:孩子id,value:父亲id),方便找到节点的路径而设置这个变量
var childToFatherMap= new HashMap();
// 节点id,节点对象映射关系
var nodeMap= new HashMap();
// 父亲节点id集合
var parents= new Array();
// 第一层的节点集合
var firstLevelNodes= new Array();
// 第一层节点的父亲的集合
var firstLevelParentIds= new Array();
GridTree.prototype._makeTable = function(tree,data)
{
//data就是json数据,实际是一个js数组!
for (var i = 0; i < data.length; i++) {
// 取出id列和parent列的内容
var _id = data[i][tableContent.idColumn];
var _parent = data[i][tableContent.parentColumn];
// 如果在父亲数组中找不到这个父亲,就将父亲节点添加到parents中去。防止了出现父亲节点重复的情况
if (findInArray(parents, '_node'+_parent) == -1)
parents.push('_node'+_parent);
// 如果已经在map中存在了该节点的父亲节点,先取出已经存在的内容,再添加新的id
if (parentToChildMap.containsKey('_node'+_parent)) {
var arr = parentToChildMap.get('_node'+_parent);
arr.push('_node'+_id);
parentToChildMap.put('_node'+_parent, arr);
} else {
var arr = new Array();
arr.push('_node'+_id);
parentToChildMap.put('_node'+_parent, arr);
}
// 添加孩子到父亲的映射关系
childToFatherMap.put('_node'+_id, '_node'+_parent);
// 添加节点id到节点的映射关系
nodeMap.put('_node'+_id, data[i]);
}
// 定义一个数组,用来存放第一层节点的父级节点...
firstLevelParentIds = removeArrayFromOtherArra
y(parents, nodeMap.keys());
// 得到节点中的父亲节点集合
parents = removeArrayFromOtherArra
y(parents, firstLevelParentIds);
// 下面根据firstLevelParentIds得到要在第一层显示的那些节点的id
for (var ii = 0; ii < firstLevelParentIds.length; ii++) {
firstLevelNodes = firstLevelNodes.concat(parentToChildMap
.get(firstLevelParentIds[ii]));
}
............
}
GridTree的动态添加表格
在上面已经形成了相关的分析数据之后,保存在了GridTree的内部对象中,即一些hashMap,array对象中,然后要做的就是根据这里的数组内容进行动态表格的添加。在程序中,有标题行和分页栏,于是使用了tBody,tHead,tFoot的结构进行添加。
关于动态表格添加的具体方法可以在网上随便搜到比较简单。
GridTree的分页实现
最先看到的网上的表格树就没有分页的功能。而我这里的表格树是前台分页,即根据全部的json数据进行分页的计算,在点击分页按钮不会到后台再查询。如果要做后台分页的话,肯定要涉及到后台的相关方法,为了简单处理,就没有实现后台分页。
在程序中定义了一个js对象,然后根据传入的json数组进行与分页信息相关的计算,代码如下:
// 分页的相关信息
var pagingInfo= new Object();
GridTree.prototype._makeTable = function(tree,data)
{。。。。。
// 总共的第一层节点数目(可以理解为信息总数)
pagingInfo.allCount = firstLevelNodes.length;
// 每页的显示信息条数
pagingInfo.pageSize = tableContent.pageSize;
// 总共的页数
pagingInfo.pagesCount = Math.ceil(pagingInfo.allCount / pagingInfo.pageSize
* 1.0);
// 当前页数(从1开始计数)
pagingInfo.currentPage = 1;
。。。
}
其他的小小心得
1.在程序中开始考虑要兼容火狐的,但是一个重要的问题是火狐下面不可以使用innerText属性。解决的办法是:
function appendText(node, txt) {
// 如果是ie浏览器
if (document.all) {
node.innerText = txt;
} else {
var nd = document.createTextNode(txt);
node.appendChild(nd);
}
}
2.关于文本缩进,使用样式 style.textIndent = '5px'就可以缩进5个像素了。在程序中不同级别显示有层次的关系时,用到了这个。