如果再加上右鍵功能,嗯,「PM,請給我幾個人月吧」!
最大的問題應該出在瀏覽器相容性,以及少了一些很好用的 function 吧。
這些問題 jQuery 都解決了,所以今天種一顆樹簡單多了。
程式有三隻:easytree.js、easytree.html 與 easytree.css,當然還有幾張 icon。
easytree.js
var bi = {};
bi.Tree = function(domId) {
this.domId = domId;
this.dom = null;
this.nodes = [];
this.nodeSn = 1;
this.isShow = false;
this.maxLevel = 5;
this.logging = true;
// 建立根目錄
this.rootNode = new bi.Folder(this, 'EasyTree', true);
this.rootNode.isRoot = true;
this.nodes[this.rootNode.nodeId] = this.rootNode;
// 好用的變數
var that = this;
// create the tree dom if not found
if ($('#' + this.domId).length == 0) {
$('body').append('<div id="' + this.domId + '"></div>');
}
this.dom = $('#' + this.domId);
// add tree dom class
this.dom.addClass('biTree');
// attachEvent - 開關目錄
$('.folderTitle').live('click', function() {
var nodeId = $(this).attr('nodeId');
var node = that.get(nodeId);
that.toggleFolder(node);
});
// attachEvent - 右鍵選單
$('.node, #rcMenu').live("contextmenu", function(e){
return false;
});
$('#rcMenu').live("mouseleave", function(e){
$(this).hide();
});
$('.node').live('mousedown', function(e) {
if (e.which != 3) {
return false;
}
var nodeId = $(this).attr('nodeId');
var node = that.get(nodeId);
var rcMenu = that.getRcMenu();
rcMenu.css({'top': e.pageY + 'px', 'left': e.pageX + 'px'});
rcMenu.empty();
if (node.children) {
// folder node
rcMenu.append('<div class="item" onclick="tree.addFile(\'New File\', ' + nodeId + ');">Add A File</div>');
rcMenu.append('<div class="item" onclick="tree.addFolder(\'New Folder\', false, ' + nodeId + ');">Add A Folder</div>');
rcMenu.append('<div class="item" onclick="tree.renameNode(' + nodeId + ');">Rename...</div>');
rcMenu.append('<div class="item" onclick="tree.deleteFolder(' + nodeId + ');">Delete</div>');
}
else {
// file node
rcMenu.append('<div class="item" onclick="tree.renameNode(' + nodeId + ');">Rename...</div>');
rcMenu.append('<div class="item" onclick="tree.deleteFile(' + nodeId + ');">Delete</div>');
}
});
/**
* 取得右鍵選單
*/
this.getRcMenu = function() {
if ($('#rcMenu').length == 0) {
$('body').append('<div id="rcMenu"></div>');
}
var rcMenu = $('#rcMenu');
rcMenu.show();
return rcMenu;
};
/**
* 計算每個 node 的位置
*/
this.calculate = function() {
$('.node').each(function(){
var p = $(this).offset();
var w = $(this).outerWidth();
var h = $(this).outerHeight();
var node = that.get($(this).attr('nodeId'));
node.area['top'] = p.top;
node.area['left'] = p.left;
node.area['bottom'] = p.top + h;
node.area['right'] = p.left + w;
});
};
/**
* 開啟或關閉目錄
*/
this.toggleFolder = function(node) {
// 取得目錄包
var fullNodeDom = node.getFullNodeDom();
// 移除舊css
fullNodeDom.removeClass('folderopen').removeClass('folderclosed');
// 修改狀態
node.open = !node.open;
// 更新css
fullNodeDom.addClass(node.open ? 'folderopen' : 'folderclosed');
node.getInsideDom().slideToggle(function(){
// 計算每個 node 的位置
that.calculate();
});
this.log();
};
/**
* 新增目錄
*/
this.addFolder = function(title, open, parentId) {
$('#rcMenu').hide();
var parentNode = null;
if (parentId) {
parentNode = this.get(parentId);
// 只能加在目錄下
if (!parentNode.children) {
alert("Can't add something under a file!");
return;
}
// 層數限制
var parents = parentNode.getNodeDom().parents('.folderInside');
if (parents.length >= this.maxLevel) {
alert("Can't add any folders deeper than this!");
return;
}
}
var f = new bi.Folder(this, title, open);
if (this.isShow) {
// tree 已經 render,必須修改html
var html = f.html();
var parentNode = this.get(parentId);
// 加到 tree 裡
parentNode.add(f);
// 加到 html 裡
parentNode.getInsideDom().append(html);
// 沒打開的目錄把它打開
if (!parentNode.open) {
this.toggleFolder(parentNode);
}
// 計算每個 node 的位置
this.calculate();
}
else {
// tree 尚未 render,先加到 tree 裡
this.add(f);
}
this.log();
return f;
};
/**
* 新增檔案
*/
this.addFile = function(title, parentId) {
$('#rcMenu').hide();
var parentNode = null;
if (parentId) {
parentNode = this.get(parentId);
// 只能加在目錄下
if (!parentNode.children) {
alert("Can't add something under a file!");
return;
}
// 層數限制
var parents = parentNode.getNodeDom().parents('.folderInside');
if (parents.length >= this.maxLevel) {
alert("Can't add any files deeper than this!");
return;
}
}
var f = new bi.File(this, title);
if (this.isShow) {
// tree 已經 render,必須修改html
var html = f.html();
// 加到 tree 裡
parentNode.add(f);
// 加到 html 裡
parentNode.getInsideDom().append(html);
// 沒打開的目錄把它打開
if (!parentNode.open) {
this.toggleFolder(parentNode);
}
// 計算每個 node 的位置
this.calculate();
}
else {
// tree 尚未 render,先加到 tree 裡
this.add(f);
}
this.log();
return f;
};
/**
* 重新命名
*/
this.renameNode = function(nodeId) {
$('#rcMenu').hide();
var node = this.get(nodeId);
var newTitle = prompt('Enter new Title', node.title);
if (!newTitle) {
return;
}
// 更新 tree 裡的 node
node.title = newTitle;
// 更新網頁
node.getNodeDom().html(node.title);
this.log();
};
/**
* 刪除目錄
*/
this.deleteFolder = function(nodeId) {
$('#rcMenu').hide();
var node = this.get(nodeId);
if (node.isRoot) {
alert("Can't delete root node!");
return;
}
if (!confirm('Are you sure?')) {
return;
}
this.doDeleteFolder(node);
};
/**
* 執行目錄刪除
*/
this.doDeleteFolder = function(node) {
// 從 tree 裡移除
var parentNode = this.get(node.getParentNodeId());
parentNode.children = $.grep(parentNode.children, function(obj){
return obj.nodeId != node.nodeId;
});
// 從網頁移除
node.getFullNodeDom().slideUp(function(){
node.getFullNodeDom().remove();
// 計算每個 node 的位置
that.calculate();
});
this.log();
};
/**
* 刪除檔案
*/
this.deleteFile = function(nodeId) {
$('#rcMenu').hide();
var node = this.get(nodeId);
if (!confirm('Are you sure?')) {
return;
}
this.doDeleteFile(node);
};
/**
* 執行檔案刪除
*/
this.doDeleteFile = function(node) {
// 從 tree 裡移除
var parentNode = this.get(node.getParentNodeId());
parentNode.children = $.grep(parentNode.children, function(obj){
return obj.nodeId != node.nodeId;
});
// 從網頁移除
node.getNodeDom().slideUp(function(){
node.getNodeDom().remove();
// 計算每個 node 的位置
that.calculate();
});
this.log();
};
/**
* 新增 node 到 tree 裡
*/
this.add = function(node) {
// 放到根目錄下
this.rootNode.children.push(node);
// 快速查詢用
this.nodes[node.nodeId] = node;
this.log();
};
/**
* 快速查詢所有 node
*/
this.get = function(nodeId) {
return this.nodes[nodeId];
};
/**
* 將 tree 顯示到網頁裡
*/
this.show = function() {
this.dom.html(this.rootNode.html());
this.isShow = true;
// 計算每個 node 的位置
this.calculate();
this.log();
};
this.log = function(msg, replace) {
if (this.logging) {
if (msg) {
if (replace) {
$('#msg').html('<br/>' + msg);
}
else {
$('#msg').append('<br/>' + msg);
}
}
else {
$('#msg').html(this.toString());
}
}
};
this.toString = function() {
var s = ' - BiTree Array In Memory - ';
$.each(this.rootNode.children, function(idx, obj){
s += "<br/>" + obj;
});
return s;
};
};
bi.Folder = function(tree, title, open){
this.nodeId = tree.nodeSn++;;
this.tree = tree;
this.title = title;
this.open = open;
// 子包
this.children = [];
// 是否為根目錄
this.isRoot = false;
// 目錄名稱 DOM 物件
this.nodeDom = null;
// 目錄包 DOM 物件
this.fullNodeDom = null;
// DOM 物件的四邊
this.area = [];
this.area['top'] = null;
this.area['left'] = null;
this.area['bottom'] = null;
this.area['right'] = null;
/**
* 加入檔案或目錄
*/
this.add = function(node) {
// 放入子包
this.children.push(node);
// 放入 tree 包,方便快速尋找
this.tree.nodes[node.nodeId] = node;
};
/**
* 取得 node 的 DOM 物件
*/
this.getNodeDom = function() {
if (!this.nodeDom) {
this.nodeDom = $('.node' + this.nodeId);
}
return this.nodeDom;
};
/**
* 取得目錄包
*/
this.getFullNodeDom = function() {
// 因為目錄結構不同於檔案,用來取得整個目錄包
if (!this.fullNodeDom) {
// 目錄名稱往上一層
this.fullNodeDom = this.getNodeDom().parent();
}
return this.fullNodeDom;
};
/**
* 取得目錄包裡的子包
*/
this.getInsideDom = function() {
return $('.folderInside' + this.nodeId);
};
/**
* 取得父層 nodeId
*/
this.getParentNodeId = function() {
return this.getNodeDom().parent().parent().attr('nodeId');
};
this.html = function() {
// 目錄包結構為
// 目錄包 folder
// - 目錄名稱 folderTitle
// - 子包 folderInside
// - 其他檔案或目錄
var html = '';
html += '<div class="folder ' + (this.open ? 'folderopen' : 'folderclosed') + '">';
html += '<div class="node folderTitle node' + this.nodeId + '" nodeId="' + this.nodeId + '">' + this.title + '</div>';
html += '<div class="folderInside folderInside' + this.nodeId + '" nodeId="' + this.nodeId + '" style="display: ' + (this.open ? 'block' : 'none') + '">';
$.each(this.children, function(idx, obj){
html += obj.html();
});
html += '</div>';
html += '</div>';
return html;
};
this.toString = function() {
var s = '[Folder ' + this.nodeId + '] ' + this.title;
$.each(this.children, function(idx, obj){
s += "<br/>" + obj;
});
return s;
};
};
bi.File = function(tree, title) {
this.nodeId = tree.nodeSn++;;
this.tree = tree;
this.title = title;
// DOM 物件
this.nodeDom = null;
// DOM 物件的四邊
this.area = [];
this.area['top'] = null;
this.area['left'] = null;
this.area['bottom'] = null;
this.area['right'] = null;
/**
* 取得 node 的 DOM 物件
*/
this.getNodeDom = function() {
if (!this.nodeDom) {
this.nodeDom = $('.node' + this.nodeId);
}
return this.nodeDom;
};
/**
* 取得父層 nodeId
*/
this.getParentNodeId = function() {
// 往上一層取 nodeId
return this.getNodeDom().parent().attr('nodeId');
};
this.html = function() {
var html = '';
html += '<div class="node file node' + this.nodeId + '" nodeId="' + this.nodeId + '">';
html += this.title;
html += '</div>';
return html;
};
this.toString = function() {
return '[File ' + this.nodeId + '] ' + this.title;
};
};
easytree.css
body {
font-size: 14px;
}
.tree {
width: 200px;
}
#msg {
position: absolute;
left: 500px;
top: 0;
margin: 10px;
}
/***** node *****/
.tree .folderclosed {
}
.tree .folderopen {
}
.tree .node {
background-repeat: no-repeat;
padding: 0 0 0 34px;
cursor: pointer;
}
.tree .node:hover {
color: #f30;
}
.tree .folder {
}
.tree .folderclosed > .folderTitle {
background-image: url(../images/folderClosed.png);
}
.tree .folderopen > .folderTitle {
background-image: url(../images/folderOpen.png);
}
.tree .folderInside {
padding: 0 0 0 17px;
}
.tree .file {
background-image: url(../images/file.png);
}
/***** rcMenu *****/
#rcMenu {
background-color: #eee;
border: 1px solid #ddd;
position: absolute;
padding: 5px;
}
#rcMenu .item {
cursor: pointer;
padding: 2px;
}
#rcMenu .item:hover {
color: #fff;
background-color: #f90;
}
easytree.html<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=BIG5">
<title>Easy Tree</title>
<link type="text/css" rel="stylesheet" href="style/easytree.css"></link>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="js/easytree.js"></script>
<script type="text/javascript">
var tree;
$(function(){
tree = new bi.Tree('easytree');
for (var i=1; i<=5; i++) {
var f = new bi.Folder(tree, "Folder_" + i, false);
f.add(new bi.Folder(tree, "Sub Folder_" + i, false));
f.add(new bi.File(tree, "File_" + i, ""));
tree.add(f);
}
for (var i=1; i<=10; i++) {
tree.addFile("File_" + (i + 5));
}
tree.show();
$('#msg').show();
});
</script>
</head>
<body>
<div id="easytree" class="tree"></div>
<div id="msg"></div>
</body>
</html>
當然,今天還是可以跟 PM 報個幾個月人力...



看到end那句 我笑了XD
回覆刪除看到end我笑了XD
回覆刪除