我们经常看到很多网站上有windows的资源管理器一样的树型菜单,对于树型结构的数据分类显示来说,非常的实用。如果想自己实现这一功能,怎么办?实际上方法很多,有的采用以开发控件的形式实行,有的则是用纯javascript来实现的,而下面我们则是以动态网页开发语言PHP结合JavaScript来实现的。
JavaScript部分很简单,主要是实现菜单展开,折叠以及图标的变换,菜单展开的时候用什么图标,折叠的时候又用什么图标。最后就是怎样打开树叶子的链接的URL地址。
而PHP部分则主要模拟HTML的DIV对象,把一个DIV对象看成是树的一个节点,而这个节点又可以有很多字节点,即一个DIV对象中,又可以加入很多子节点DIV对象,每个子节点DIV对象又可以加入很多孙节点,如此反复,既可以得到一棵树型的菜单,最顶层的是跟节点,也就是树干了,然后每个树枝(子节点)上的也可能还有子树枝的节点,这些节点就是树干的子树干,没有子树枝的节点就是树叶子,这就是树的果实(内容)部分了。然后在对每个节点(DIV对象)设置属性和样式值来好对其操作和显示控制。
我们则以一个关系数据表的内容为例,实现了从表到树型菜单的转换。
以下是程序源代码。
<SCRIPT language=javascript>
<!--
function showChildren(obj,tab){
objs = obj.children.tags("DIV");
if (objs == null || objs.length == 0) {
return;
}
/*if(img.src == "images/folder.gif")
img.src = "images/link0.gif";
else
img.src = "images/folder.gif";*/
setImage(obj);
if (objs[0].style.display == "block") {
len = objs.length;
for (i = 0; i < len; i++) {
objs[i].style.display = "none";
}
} else {
len = objs.length;
for (i = 0; i < len; i++) {
objs[i].style.display = "block";
} }
}
function setImage(obj) {
imgs = obj.all.tags("IMG");
folderOn = "images/folder.gif";
folderOff = "images/folder2.gif";
if (imgs.length > 1) {
str = imgs[0].src;
if (imgs[0].src.lastIndexOf(folderOn) != -1) {
imgs[0].src = folderOff;
} else {
imgs[0].src = folderOn;
}
} else {
if (imgs.src.lastIndexOf(folderOn) != -1)
imgs.src = folderOff;
else
imgs.src = folderOn;
}
}
function showURL(obj,url){
window.open(url);
}
-->
</SCRIPT>
<?PHP
function tab($n,$str) {
$ret = "";
for ($i = 0; $i < $n; $i++)
$ret .= $str;
return $ret;
}
//对子节点数组进行排序,其中有子节点的节点优先
function sortChildren($child1,$child2) {
$c1 = $child1->hasChildNodes();
$c2 = $child2->hasChildNodes();
if ($c1) {
if ($c2) {
if ($child1->innerText === $child2->innerText)
return 0;
else
return ($child1->innerText > $child2->innerText)?1:-1;
} else {
return -1;
}
} else {
if ($c2) {
return 1;
} else {
if ($child1->innerText === $child2->innerText)
return 0;
else
return ($child1->innerText > $child2->innerText)?1:-1;
}
}
}
/************************************************
* 类名:DIV
* 模拟HTML的DIV对象的类
*************************************************/
class DIV {
var $innerText; //<DIV>和</DIV>之间的文本
var $innerHTML; //<DIV>和</DIV>之间的包括HTML标记的文本
var $children; //子DIV节点类,关键字是innerText
var $indent; //树的深度
var $id; //id
var $attributes; //属性
var $imgLeaf; //最下层节点(树叶)的图标
var $url; //最下层节点(树叶)的URL地址
var $imgFolderOn; //节点展开是的图标
var $imgFolderOff; //节点折叠是的图标
var $outerHTML; //从<DIV>开始到</DIV>结束的HTML文本
var $parent; //父节点
/*********************************
* 构造函数
* 变量初始化
**********************************/
function DIV()
{
$this->children = array();
$this->attributes = array();
$this->setId($this->genID());
$this->setInnerText($this->genID());
$this->setIndent(0);
$this->setImgLeaf("images/link0.gif");
$this->setImgFolderOn("images/folder.gif");
$this->setImgFolderOff("images/folder2.gif");
$this->url = "";
}
/********************************
* 生成DIV对象的ID值
*
*********************************/
function genID() {
$abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
$nLen = strlen($abc);
$LEN = 20;
$id = "";
for ($i = 0; $i < $LEN; $i++) {
mt_srand((double)microtime()*1000000); //设定种子
$randval = mt_rand(); //生成随机值
if($i == 0)
$id .= substr($abc,$randval % ($nLen - 10),1); //第一位设置成英文字母
else
$id .= substr($abc,$randval % $nLen,1);
}
return $id;
}
/********************************
* 设置叶子节点的图标imgLeaf
* $value:图标的路径
*********************************/
function setImgLeaf($value) {
$this->imgLeaf = $value;
}
/********************************
* 返回叶子节点的图标路径imgLeaf
* return:叶子节点的图标路径
*********************************/
function getImgLeaf() {
return $this->imgLeaf;
}
/********************************
* 设置节点展开的图标的路径imgFolderOn
* $value:节点展开图标的路径
*********************************/
function setImgFolderOn($value) {
$this->imgFolderOn = $value;
}
/********************************
* 返回节点展开图标的路径imgFolderOn
* return:节点展开图标的路径
*********************************/
function getImgFolderOn() {
return $this->imgFolderOn;
}
/********************************
* 设置节点折叠的图标路径imgFolderOff
* $value:节点折叠的图标路径
*********************************/
function setImgFolderOff($value) {
$this->imgFolderOff = $value;
}
/********************************
* 返回节点折叠是的图标路径imgFolderOff
* return:节点折叠是的图标路径
*********************************/
function getImgFolderOff() {
return $this->imgFolderOff;
}
/********************************
* 设置innerText
* $value:DIV对象之间的文本
*********************************/
function setInnerText($value) {
$this->innerText = $value;
}
/********************************
* 返回innerText
* return:DIV对象之间的文本
*********************************/
function getInnerText() {
return $this->innerText;
}
/********************************
* 设置父节点
* $value:父节点对象
*********************************/
function setParent($value) {
$this->parent = $value;
}
/********************************
* 返回父节点
*********************************/
function getParent() {
return $this->parent;
}
/********************************
* 返回包括属性在内的开始DIV标记的内容
*
*********************************/
function getStartTag() {
$str = "";
while(list($name,$value) = each($this->attributes)) {
$str .= " $name='$value'";
}
$str =tab($this->getIndent(),"/t") . "<DIV$str>/r/n";
return $str;
}
/********************************
* 返回结束DIV标记的内容
*********************************/
function getEndTag() {
return tab($this->getIndent(),"/t") . "</DIV>/r/n";
}
/********************************
* 设定DIV标记的属性值
* $name:属性名
* $value:值
*********************************/
function setAttribute($name,$value) {
$this->attributes[$name] = $value;
}
/********************************
* 返回指定属性的值
* $name:属性名
* return:属性值
*********************************/
function getAttribute($name) {
return $this->attributes[$name];
}
/********************************
* 设置DIV对象的ID
* $value:ID值
*********************************/
function setId($value) {
$this->setAttribute("id",$value);
}
/********************************
* 返回DIV对象的ID值
* return:ID值
*********************************/
function getId() {
return $this->getAttribute("id");
}
/********************************
* 设置父节点跟字节点的缩进值indent
* $value:缩进值
*********************************/
function setIndent($value) {
$this->indent = $value;
}
/********************************
* 返回缩进值indent
* return:缩进值
*********************************/
function getIndent() {
return $this->indent;
}
/********************************
* 判断是否有字节点
* return:true-有、false-无
*********************************/
function hasChildNodes() {
return (count($this->children) != 0);
}
/********************************
* 添加字节点对象
* $child:字节点对象
*********************************/
function insertChild($child) {
$innerText = $child->getInnerText();
//判断该字节点是否存在
$myChild = $this->getChild($child);
if (!isset($myChild)) {//如果不存在的话,作为新节点添加进去
$this->children[$innerText] = $child;
$this->children[$innerText]->setParent($this);
$this->children[$innerText]->setIndent($this->getIndent() + 1);
}
//返回该字节点
return $this->children[$innerText];
}
/********************************
* 返回指定的字节点
* $child:字节点
* return:字节点
*********************************/
function getChild($child) {
$innerText = $child->getInnerText();
return $this->children[$innerText];
}
/********************************
* 返回字节点数组
*********************************/
function getChildren() {
return $this->children;
}
/********************************
* 设置属性
* $name:属性名称
* $value:属性值
*********************************/
function isExist($child) {
$t = $child->getInnerText();
while(list($id,$myChild) = each($this->children)) {
$t2 = $myChild->getInnerText();
if($t === $t2)
return true;
}
return false;
}
/********************************
* 比较两个DIV对象的ID值
* return:true-相等
*********************************/
function equal($obj) {
if (($this->getId() === $obj->getId()) &&
($this->getInnerText() === $obj->getInnerText()))
return true;
return false;
}
/*******************************
* 对字节点数组进行排序
*******************************/
function childrenSort() {
usort($this->children,sortChildren);
}
/********************************
* 返回DIV标记之间的HTML文本
*********************************/
function getInnerHTML() {
//获取属性信息
$indent = $this->getIndent();
$myId = $this->getId();
$innerText = $this->getInnerText();
$imgLeaf = $this->getImgLeaf();
$imgFolderOn = $this->getImgFolderOn();
$imgFolderOff = $this->getImgFolderOff();
//该节点是否有字节点
if ($this->hasChildNodes()) {
$this->childrenSort();
if ($indent > 0)
$html = tab($indent + 1,"/t") . tab($indent," ") . "<IMG src='$imgFolderOn'><A href='javascript:showChildren($myId,$indent)'>" . $innerText . "</A>/r/n";
else
$html = tab($indent + 1,"/t") . tab($indent," ") . "<IMG src='$imgFolderOff'><A href='javascript:showChildren($myId,$indent)'>" . $innerText . "</A>/r/n";
$children = $this->getChildren();
while(list($id,$child) = each($children)) {
if ($indent > 0) {
$child->setAttribute("style","display:none;");
$html .= $child->getOuterHTML();
// $html .= $child->getStartTag() . $child->getInnerHTML() . $child->getEndTag();
} else {
$child->setAttribute("style","display:block;");
$html .= $child->getOuterHTML();
// $html .= $child->getStartTag() . $child->getInnerHTML() . $child->getEndTag();
}
}
} else {//树叶
$html = tab($indent + 1,"/t") . tab($indent," ") . "<IMG src='$imgLeaf'><A href='javascript:showURL($myId,/"$this->url/")'>" . $innerText . "</A>/r/n";
}
return $html;
}
/********************************
* 返回包含DIV标记在内的HTML文本
*********************************/
function getOuterHTML() {
return $this->getStartTag() . $this->getInnerHTML() . $this->getEndTag();
}
function getChildrenHTML() {
$html = "";
$this->childrenSort();
$children = $this->getChildren();
while(list($id,$child) = each($children)) {
$html .= $child->getOuterHTML();
}
return $html;
}
/********************************
* 分割文本的类型$parttern
* "A;B;C;D;..."转换后→
* A→
* B→
* C→
* D→
* ...
*
*********************************/
function insertList($parttern,$itemList) {
$items = split($parttern,$itemList);
$nCnt = count($items);
if ($nCnt > 1) {
$div = new DIV;
$div->setInnerText($items[0]);
$list = substr($itemList,strpos($itemList,";") + 1);
$this->insertChild($div);
$this->children[$items[0]]->insertList($parttern,$list);
} else {
$div = new DIV;
$div->setInnerText($itemList);
$this->insertChild($div);
}
}
}
/****************************************************
* 这是一个例子。
* 数据类似对关系数据表的结构。
****************************************************/
$tr = array(
array("first","second","third","forth"),
array("first","second","third","fifth"),
array("first","second","third","sixth"),
array("first","third","forth","fifth"),
array("first","third","third","fifth"),
array("first","forth","third","fifth"),
array("second","first","first","first"),
array("second","second","HAHAH","TEST"),
array("first","OK","first","first"));
$n1 = count($tr);//计算数组的记录数
$root = new DIV;//树的根
for ($i = 0; $i < $n1; $i++) {
$td = $tr[$i];
$n2 = count($td);
$parent = "";
$tmp = "";
for ($j = 0; $j < $n2; $j++) {
// echo "<br> $n2";
$value = $td[$j];
$divObj = new DIV;
$divObj->setInnerText($value);//设置文本
//添加子节点
/**********************************
数组的元素A,B,C,D,...按照下面的形式进行转换
A→
B→
C→
D→
...
***********************************/
if ( $j == 0) {
$root->insertChild($divObj);
} else if ($j == 1) {
$root->children[$td[0]]->insertChild($divObj);
} else if ($j == 2) {
$root->children[$td[0]]->children[$td[1]]->insertChild($divObj);
} else if ($j == 3) {
$root->children[$td[0]]->children[$td[1]]->children[$td[2]]->insertChild($divObj);
}
}
}
echo count($root->getChildren()) . "<br>";
// echo "/r/n" . $root->getOuterHTML();
reset($root);
$root = new DIV;
for ($i = 0; $i < $n1; $i++) {
$record = implode(";",$tr[$i]);
/**********************************
数组的元素A,B,C,D,...按照下面的形式转换称字符串
"A;B;C;D;..." ...
***********************************/
$root->insertList(";",$record);
}
$root->insertList(";","sakdfk;ksdkfksa;skdfk;skdflsa;ksadkf;kak;sdfd");
// echo "/r/n" . $root->getOuterHTML();
?>