前言:
javascript虽然简单,但是要用它以面向对象的方式写出优雅的代码还是需要一定的功底的,据我了解很多从事Web开发的程序员对javascript的掌握基本上停留在表单校验等常用技巧方面,对如何将javascript组件化还是云里雾里。
目标:
通过用javascript编写一个tree来讲解javascript和dhmtl的一些知识,重点放在如何将javascript如何对象化及绑定事件等方面。先用html和javascript编写一个简单的tree,然后将它封闭成简单控件,然后再为这个简单控件添加复杂的功能,直到做成一个还算漂亮的控件。
注:本人在文章中难免出现一些错漏之处,请多多包涵,并欢迎指正。
如果您没有耐心守在我的博客等我的连载,也可以直接下载最终的源代码,地址是:
目前源代码中还有一些未实现的功能,并且存在一些写得不够优雅的地方,不排除会在后面进行修改。
第一步:从html开始构思一个tree
为了简洁美观,我们采用表格的方式来布局树,树的每一个节点都是一个表格,这个表格分为两行,第一行三个单元格,第一、二单元格宽度均为20像素,分别放置节点的控制图标和文件夹图标,第三个单元格宽度不定,里面放置一个div用于显示节点的标题。为了使标题太长时不换行,我们给这个div加一个属性:<div nowrap="noWrap">。
表格的第二行只有一个单元格,用来放置子节点,也就是在这个单元格里再递归式的嵌套一个和外层节点一样的表格。
为了使子节点相对父节点有点缩进,我们给这个单元格定义一个样式:style='padding-left:20px' ,使左边缩进20像素。
具体布局如下图(注:表格实际上是没边框的,为了看起来直观我给加上了边框)
下面是相关的html代码:
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td><table cellspacing="0" cellpadding="0" width="100%" border="0">
<tbody>
<tr>
<td width="20"><img onClick="javascript:expandNode(this)" height="18" src="images/minus.gif" width="18" /></td>
<td width="20"><img height="18" src="images/folderClosed.gif" width="18" /></td>
<td><div nowrap="noWrap">计算机应用专业</div></td>
</tr>
</tbody>
</table></td>
</tr>
<tr>
<td style='padding-left:20px'>
<!--子节点开始位置-->
<!--子节点结束位置--> </td>
</tr>
</tbody>
</table>
显示效果如下图:
在上面代码中,我们给节点控制图片加了一个onclick事件,调用expandNode函数,下面我们来实现这函数。
这个函数的功能是当点击图标时,显示或隐藏子节点:
- 当显示子节点时控制图片变成 "-" 号,文件夹图片变为打开;
- 当隐藏子节点时控制图片变成 "+" 号,文件夹图片变为关闭。
下面是该函数的代码:
<script language="javascript">
var IMG_PLUS="images/plus.gif";
var IMG_MINUS="images/minus.gif";
var IMG_FOLDER_OPEN="images/folderOpen.gif";
var IMG_FOLDER_CLOSE="images/folderClosed.gif";
function expandNode(img){
//获取放置子节点的行
var nodeTbody=img.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
//获取文件夹图片
var folderImg= img.parentNode.nextSibling.firstChild;
//当该行处于隐藏状态时将它显示
if(nodeTbody!=null){
if(nodeTbody.rows[1].style.display == "none"){
nodeTbody.rows[1].style.display = "";
//图标变为 "-" 号
img.src = IMG_MINUS;
//文件夹图片变为打开
folderImg.src=IMG_FOLDER_OPEN;
}
else{
//反之如果处于显示状态则隐藏
nodeTbody.rows[1].style.display = "none";
//图标变为 "+" 号
img.src = IMG_PLUS;
//文件夹图片变为关闭
folderImg.src=IMG_FOLDER_CLOSE;
}
}
}
</script>
因为树的节点有很多,所以我们就不给每个节点的一些元素(比如控制图片、放置子节点的单元格等)指定ID,这给我们获取相关元素带来了麻烦,比如代码中就用了7级 "parentNode" 来获取放置子节点的行,够让人头晕吧? 但我发誓,以后封装成控件时绝对不会用这么别扭的方式,您就暂时忍一下吧。
好了,现在我们将这个节点的源码复制一下粘贴到它的子节点处(呵呵,这就是手动的递归嵌套),全部代码变成了这样的:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>无标题文档</title>
<script language="javascript">
var IMG_PLUS="images/plus.gif";
var IMG_MINUS="images/minus.gif";
var IMG_FOLDER_OPEN="images/folderOpen.gif";
var IMG_FOLDER_CLOSE="images/folderClosed.gif";
function expandNode(img){
//获取放置子节点的行
var nodeTbody=img.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
//获取文件夹图片
var folderImg= img.parentNode.nextSibling.firstChild;
//当该行处于隐藏状态时将它显示
if(nodeTbody!=null){
if(nodeTbody.rows[1].style.display == "none"){
nodeTbody.rows[1].style.display = "";
//图标变为 "-" 号
img.src = IMG_MINUS;
//文件夹图片变为打开
folderImg.src=IMG_FOLDER_OPEN;
}
else{
//反之如果处于显示状态则隐藏
nodeTbody.rows[1].style.display = "none";
//图标变为 "+" 号
img.src = IMG_PLUS;
//文件夹图片变为关闭
folderImg.src=IMG_FOLDER_CLOSE;
}
}
}
</script>
</head>
<body>
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td><table cellspacing="0" cellpadding="0" width="100%" border="0">
<tbody>
<tr>
<td width="20"><img οnclick="javascript:expandNode(this)" height="18" src="images/plus.gif" width="18" /></td>
<td width="20"><img height="18" src="images/folderClosed.gif" width="18" /></td>
<td><div nowrap="noWrap">计算机应用专业</div></td>
</tr>
</tbody>
</table></td>
</tr>
<tr>
<td style='padding-left:20px'>
<!--子节点开始位置-->
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td><table cellspacing="0" cellpadding="0" width="100%" border="0">
<tbody>
<tr>
<td width="20"><img οnclick="javascript:expandNode(this)" height="18" src="images/plus.gif" width="18" /></td>
<td width="20"><img height="18" src="images/folderClosed.gif" width="18" /></td>
<td><div nowrap="noWrap">计算机应用专业</div></td>
</tr>
</tbody>
</table></td>
</tr>
<tr>
<td style='padding-left:20px'>
<!--子节点开始位置-->
<!--子节点结束位置--> </td>
</tr>
</tbody>
</table>
<!--子节点结束位置--> </td>
</tr>
</tbody>
</table>
</body>
</html>
页面显示效果如下图:
好了到此为止,一颗最简单的树已成生成了。
下一步我们将开始将它封装成面向对象的javascript组件。
未完待续 ......