
6.2 树视图的最简化实现
根据前面的分析,本节就来完成树视图的最简化实现,同时完成树视图最基本的功能需求。
6.2.1 树视图的HTML结构和数据结构
在页面上只需要一个<div>标签作为树视图的最外层容器,让生成的树视图都放到里面,外观则另由CSS代码控制。本节目标是“最简化实现”,所以下面并没有加入外观控制之类的代码,JavaScript代码中也暂不考虑外观控制。
<! DOCTYPE html> <html> <head> <title>javascript base tree</title> </head> <body> <div id="mytree"></div> </body> </html> <script src="base.js"></script>
其中base.js文件是之前用过的一些基础代码集合。树视图的每个节点通常都有一个唯一的标识符,这里将其定义为JavaScript对象的键名,它对应的父节点定义为pid,用以关联相互之间的关系,用cn表示其中文名称,用url来存储可能的链接地址。在上面的代码后追加如下数据:
<script> var dic = { "0" : {pid:-1, cn:’本书目录’, url:'/'} ,"1" : {pid:0, cn:’第1 章 JavaScript 概述’, url:'/01'} ,"2" : {pid:0, cn:’第2 章 用JavaScript 验证表单’, url:'/02'} ,"11" : {pid:1, cn:'1.1 认识JavaScript', url:'#'} ,"12" : {pid:1, cn:'1.2 配置JavaScript 开发环境’, url:'#'} ,"3" : {pid:0, cn:’第3 章 JavaScript 实现的照片展示’, url:'/03'} ,"21" : {pid:2, cn:'2.1 最简单的表单验证 - 禁止空白的必填项目’, url:'#'} ,"22" : {pid:2, cn:'2.2 处理各种类型的表单元素’, url:'#'} ,"23" : {pid:2, cn:'2.3 输入的邮箱地址正确吗?用正则来校验复杂的格式要求 ', url:'#'} ,"24" : {pid:2, cn:'2.4 改善用户体验’, url:'#'} ,"31" : {pid:3, cn:'3.1 功能设计’, url:'#'} ,"32" : {pid:3, cn:'3.2 照片加载与定位’, url:'#'} ,"33" : {pid:3, cn:'3.3 响应鼠标动作’, url:'#'} }; </script>
这些数据以“本书目录”结构为基础,利用JavaScript内置对象特性构建。由于数据存储可能是乱序的,这里也是乱序设置,所以接下来要对其做序列化处理。另外,在使用这些数据之前,还应该把某个节点下的子类编号先列出来,存放在节点的child数组里。
for(var i in dic){ //用来处理所属关系 if(dic[i].pid ! ==undefined){ //判断是指定的pid 才处理 var pid = dic[i].pid; if(dic[pid]){ //判断父类是否存在 dic[pid].child || (dic[pid].child = []); //判断父类有无child,无则初始化 dic[pid].child.push(i); //登记到父类child 中 } } }
6.2.2 用递归最简化显示树
递归理论起源于哥德尔、邱奇、图灵、克莱尼和Emil Post在20世纪30年代的工作,其中克莱尼还是正则表示法的发明者。
树的显示要做的事情就是从某个节点开始,遍历其所有子节点,然后判断子节点是否还有子节点,如果有,则继续遍历子节点的子节点,如此反复,直到结束。所以这种情况只有用递归方式才能完成。
在网页中罗列数据最好的标签就是<ul>,所以同在一个父节点下的数据都应该包含在<ul>标签里,请看【范例6-1】中的最终代码。
【范例6-1 树视图的最简化实现】
1. <! DOCTYPE html> 2. <html> 3. <head> 4. <title>javascript base tree</title> 5. </head> 6. <body> 7. <div id="mytree"></div> 8. </body> 9. </html> 10. <script src="../base.js"></script> 11. <script> 12. var dic = { 13. "0" : {pid:-1, cn:’本书目录’, url:'/'} 14. , "1" : {pid:0, cn:’第1 章 JavaScript 概述’, url:'/01'} 15. , "2" : {pid:0, cn:’第2 章 用JavaScript 验证表单’, url:'/02'} 16. , "11" : {pid:1, cn:'1.1 认识JavaScript', url:'#'} 17. , "12" : {pid:1, cn:'1.2 配置JavaScript 开发环境’, url:'#'} 18. , "3" : {pid:0, cn:’第3 章 JavaScript 实现的照片展示’, url:'/03'} 19. , "21" : {pid:2, cn:'2.1 最简单的表单验证 - 禁止空白的必填项目’, url:'#'} 20. , "22" : {pid:2, cn:'2.2 处理各种类型的表单元素’, url:'#'} 21. , "23" : {pid:2, cn:'2.3 输入的邮箱地址正确吗?用正则来校验复杂的格式要求 ', url:'#'} 22. , "24" : {pid:2, cn:'2.4 改善用户体验’, url:'#'} 23. , "31" : {pid:3, cn:'3.1 功能设计’, url:'#'} 24. , "32" : {pid:3, cn:'3.2 照片加载与定位’, url:'#'} 25. , "33" : {pid:3, cn:'3.3 响应鼠标动作’, url:'#'} 26. }; //因为无序排列,下面必须做依赖关系处理 27. //这种依赖也可以由提供数据的后端来处理 28. for(var i in dic){ //用来处理所属关系 29. if(dic[i].pid ! ==undefined){//判断是指定的pid 才处理 30. var pid = dic[i].pid; 31. if(dic[pid]){ //判断父类是否存在 32. dic[pid].child || (dic[pid].child = []); //判断父类有无child,无则初始化 33. dic[pid].child.push(i); //登记到父类child 中 34. } 35. } 36. } 37. var z3fTree = function(el, pid){ 38. var ul = document.createElement("ul"); //创建一个ul 元素 39. for(var i in dic){ //遍历数据 40. if(dic[i].pid == pid){ //判断节点是否都是同一个父节点,即是否是当前需要显示的节点 41. var dl = dic[i] //取得一个节点的信息 42. var li=document.createElement("li"); //创建一个li 元素 43. li.innerHTML = '<a href="'+dl.url+'">'+dl.cn+'</a>'; //拼接html 44. if(dl.child && dl.child.length>0){//判断是否还有子类 45. z3fTree(li, i.toString()); //递归下去 46. } 47. ul.appendChild(li); //把拼装好的li 追加到ul 中去 48. }else{ 49. continue; //继续下一个循环 50. } 51. } 52. el.appendChild(ul); //插入到给定的元素中 53. }; 54. z3fTree(eg.$("mytree"), -1); 55. </script>
第37~53行的函数z3fTree()接受两个参数,el是给定的元素,pid是指定的父节点。第一次调用时,给定元素是页面上id为mytree的<div>标签,指定父节点从-1开始。后续的递归传递进去的参数就是程序自动加进去的。把代码保存为html文件,然后在浏览器中打开,效果如图6-2所示。

图6-2 最简化的目录树视图