MzTreeView节点树(梅花雪)
生活随笔
收集整理的這篇文章主要介紹了
MzTreeView节点树(梅花雪)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
MzTreeView 一次加載數據的樹[@more@]
在 MzTreeView 里都有一個虛的根節點,其ID為0,用戶可見的根節點其父節點ID皆為0
MzTreeView 1.0在數據庫庫表里的字段名稱:
特注:每個字段值中不可有冒號: 不可以換行 引號酌情考慮應不與節點字符串的引號相沖突設計模式 為了達到能夠在瀏覽器中快速打開多節點樹的頁面,我做了很多的優化與創新,下面我將詳細解說幾項最重要的部分:
MzTreeView 在客戶端的節點所擁有的屬性:
方法 MzTreeView 類的一些方法:
示例
//MzTreeView1.0網頁樹類, 在實例化的時候請把實例名作參數傳遞進來
function MzTreeView(Tname)
{
if(typeof(Tname) != "string" || Tname == "")
throw(new Error(-1, '創建類實例的時候請把類實例的引用變量名傳遞進來!'));
//【property】
this.url = "#";
this.target = "_self";
this.name = Tname;
this.wordLine = false;
this.currentNode = null;
this.useArrow = true;
this.nodes = {};
this.node = {};
this.names = "";
this._d = "x0f";
this.index = 0;
this.divider = "_";
this.node["0"] =
{
"id": "0",
"path": "0",
"isLoad": false,
"childNodes": [],
"childAppend": "",
"sourceIndex": "0"
};
this.colors =
{
"highLight" : "#0A246A",
"highLightText" : "#FFFFFF",
"mouseOverBgColor" : "#D4D0C8"
};
this.icons = {
L0 : 'L0.gif', //┏
L1 : 'L1.gif', //┣
L2 : 'L2.gif', //┗
L3 : 'L3.gif', //━
L4 : 'L4.gif', //┃
PM0 : 'P0.gif', //+┏
PM1 : 'P1.gif', //+┣
PM2 : 'P2.gif', //+┗
PM3 : 'P3.gif', //+━
empty : 'L5.gif', //空白圖
root : 'root.gif', //缺省的根節點圖標
folder : 'folder.gif', //缺省的文件夾圖標
file : 'file.gif', //缺省的文件圖標
exit : 'exit.gif'
};
this.iconsExpand = { //存放節點圖片在展開時的對應圖片
PM0 : 'M0.gif', //-┏
PM1 : 'M1.gif', //-┣
PM2 : 'M2.gif', //-┗
PM3 : 'M3.gif', //-━
folder : 'folderopen.gif',
exit : 'exit.gif'
};
//擴展 document.getElementById(id) 多瀏覽器兼容性
//id 要查找的對象 id
this.getElementById = function(id)
{
if (typeof(id) != "string" || id == "") return null;
if (document.getElementById) return document.getElementById(id);
if (document.all) return document.all(id);
try {return eval(id);} catch(e){ return null;}
}
//MzTreeView 初始化入口函數
this.toString = function()
{
this.browserCheck();
this.dataFormat();
this.setStyle();
this.load("0");
var rootCN = this.node["0"].childNodes;
var str = "";
if(rootCN.length>0)
{
this.node["0"].hasChild = true;
for(var i=0; istr += this.nodeToHTML(rootCN[i], i==rootCN.length-1);
setTimeout(this.name +".expand('"+ rootCN[0].id +"', true); "+
this.name +".focusClientNode('"+ rootCN[0].id +"'); "+ this.name +".atRootIsEmpty()",10);
}
if (this.useArrow) //使用方向鍵控制跳轉到上級下級父級子級節點
{
if (document.attachEvent)
document.attachEvent("onkeydown", this.onkeydown);
else if (document.addEventListener)
document.addEventListener('keydown', this.onkeydown, false);
}
return ""οnclick='"+ this.name +".clickHandle(event)' "+
"οndblclick='"+ this.name +".dblClickHandle(event)' "+
">"+ str +"
好東西當然要與大家分享。。。@與羊共舞的狼
MzTreeView 1.0 from--->>> http://www.meizz.com/
開發文檔: http://www.meizz.com/Web/Article.asp?id=436
控件下載: http://www.meizz.com/Web/Download/MzTreeView10.rar
應用示例: http://www.meizz.com/Web/Demo/MzTreeView10.htm
在 MzTreeView 里都有一個虛的根節點,其ID為0,用戶可見的根節點其父節點ID皆為0
MzTreeView 1.0在數據庫庫表里的字段名稱:
| id | 節點ID(不可為0,可以是數字或字符) |
| parentId | 本節點的父節點ID(若本節點已為根節點,此處填0) |
| text | 節點的顯示文本(一般不允許為空,不過有一種情況例外,即根節點,若根節點文本為空,這個根節點將不會在頁面里顯示) |
| hint | 節點的說明注解 |
| icon | 節點的圖標,MzTreeView 1.0允許每個節點擁有不同的圖標(對應的名字在類里的icons和iconsExpand定義) |
| data | 節點掛的數據,格式是 param=value¶m=value&... url里?后的那串字符串格式, |
| url | 每個節點允許擁有不同的鏈接,它為空或者為#時,樹里這個節點的鏈接,點擊將無反應 |
| target | 每個節點的鏈接允許在不同的target里打開,為空時取類里的默認值 |
| method | 點擊該鏈接時所想觸發的腳本語句 |
- 數據一次性加載 首先我要說的就是數據的一次性加載。在目前的 B/S 架構開發中對于多節點多層次的樹,特別是樹節點量超過兩千的情況下,幾乎都是采取數據異步加載來達到目的,即用戶需要展開某個節點時,再從服務器端加載下級子節點的數據,數據異步加載最為經典的例子就是 MSDN 網站左邊的目錄樹了。異步加載的優點在于可以擴充到無限級無限節點樹,樹的數據來源可以多樣化(數據庫或XML文件)等,但是它的缺點也是非常多的:設計模式比數據一次性加載要復雜得多,要考慮到 Browser/Server 之間的應答,要判斷子節點是否含有孫節點,后臺數據源的層級關系模型等。對網絡傳輸的信賴性太大,每個節點的展開都需要連一次 Server,只要在取某節點數據時網絡出現問題,就會導致該節點及其以下的子節點加載失敗。而采取數據一次加載的模式只要一次加載成功,服務器就可以不用管它了,服務器壓力減輕,腳本設計則完全獨立,對整棵樹節點的檢索可以在客戶端完成,節點展開響應速度快等等優勢,因此在節點數不多的情況下數據一次性加載更有優勢,那么這個節點數不多不多到底多少節點量為平衡點呢?就 ASP.net 里帶的那個 TreeView 來說,在一兩千節點以下一次性加載比較具有優勢,而 MzTreeView 1.0 在節點量三萬至五萬以非常具有優勢。
- 節點信息的壓縮傳輸 在瀏覽器里顯示的樹結構其實都是一個個 HTML 元素組合起來的,在 WEB 頁面里的樹都是根據樹節點的信息組合成一串的 HTML 元素列來顯示,這一步從節點信息到 HTML 的轉化可以在兩個地方生成:一個是在服務器端,一個是在客戶端。在服務器端生成的優點在于不須考慮客戶端的瀏覽器的兼容性,節點信息的可控性非常強,但是它的缺點也是非常大的:加重服務器的負擔,增加網絡傳輸量。在服務器端直接生成樹節點的 HTML 給服務器帶來的壓力是顯而易見的,也是非常巨大的,估計只要有幾十個并發連接就能讓服務器暫時停止響應了。這種直接在服務器生成樹的做法在實際運用環境中也有人使用,不過本人非常不贊成這種做法。當然也有人直接將樹生成為一個靜態文件放在服務器端,這種做法對于樹節點相對固定不變的情況還是非常有利的,只需要生成一次,以后就不需要再生成了,服務器的壓力也非常小,但它的弊病在于可變化性太小,比如說不同的權限能看到的樹節點的不同這種情況,用這種生成靜態樹放在服務器端的做就沒有辦法解決,且不管是服務器端動態計算還是直接生成靜態樹文件放在服務器端都有一個避免不了的問題就是網絡傳輸量巨大。可以計算一下,一個樹點所需要的HTML字符量大約300到600字節之間。即含有一千節點的樹的網頁大小就有300K到600K,給網絡造成的壓力是非常巨大的,所以MzTreeView 1.0采用了節點信息的壓縮傳輸,大至一千節點的總傳輸量在30K左右,這可以差了一個數量級呀。本樹將每個節點所必要的信息組合成一個字符串序列,傳遞到客戶端,然后在客戶端再用客戶端腳本對這些信息進行分析,再生成一個個的節點HTML,這也符合了WEB的分散計算的原理,當然服務器端可以有選擇性輸出部分節點,這樣又做到節點的靈活多變性。
- 傳輸的節點信息的可擴展性 服務器端將節點的必要信息組合成一個字符串序列傳遞到客戶端,然后客戶端再用腳本對這個字符串序列進行分析,生成樹的節點,那么這個字符串序列對整個樹的生成的效率就有重要的影響了。我也參照過很多組串傳輸的例子,在一般的做法當中大多采用函數的參數方式傳遞。比如說定義一個函數 funName(p1, p2, p3, ...),然后服務器組串的時候就按位置給定數據。這種組串的弊病是非常大的,首先就是位置絕對錯不得,只要有一個位置數據出錯,這個節點的信息就亂了,對于那些在函數里已經定義的但節點里沒有的信息也得用空字符串補上,諸如:(p1, "", p3, p4, "", "", ""),且萬一這種組串的對應分析函數發生了變化,那么這種串就算是廢了,得重新定義服務器端的字符串位置序列了,可以說這種組串的方式可擴展性極差。
節點信息組串傳輸的另一種常用模式就是XML。XML以它的無限可擴展性已慚有代替HTML稱霸WEB的味道了。XML最大的優點就在于它的無限可擴展性,可以用任意的TagName名,可以有任意的Attribute名,節點與子節點已經有層級的關系,用XML來做WEB樹的數據源其實是最理想的,MSDN的資源目錄樹就是采用XML作為傳遞的字符串,它唯一的不足之處就是不是所有瀏覽器都能很好地支持它,特別是在一些低版本的瀏覽器中,所以我只好忍痛割愛沒有啟用XML作為中間的傳媒。那么是不是有可能結合XML的擴展性對第一種組串的方式進行改進呢?當我愁眉不展的時候,HTML里的STYLE樣式表寫法跳入我眼,樣式的寫法是"param1: value1; param2: value3; ...",哈哈,這不是現成地給我指明了路嗎?這種寫法擁有XML的可擴展性,位置順序的隨意性,又是傳統的長字符串,正合我意也!服務器給定這種數據源字符串,我不光可以在TreeView里用它,還可以直接做Outlook Bar,下拉式層級菜單,右鍵層級菜單的數據源,豁然開朗也!我寫了一個函數專門解析這種文本:getAttribute()。 - 客戶端節點數據的存儲方式及快速檢索 現在數據源準備好了,數據傳輸也已做到最大優化了,下面就是客戶端的腳本解析了,而這一步也正是樹生成的效率瓶頸所在。由于我沒有采用XML做為數據源,所以我這里就不討論XML+XSL和XMLDOM的模式,而只考慮HTML+DOM模式了。在HTML+DOM模式下客戶端存儲的方式有很多種,我就曾經看到過一種直接將字符串輸出在多行文本框里的,但歸究起來最常用的方式就是用一個數組來存放節點信息,不過就算是用數組也是種類多多,比如說數組里套數組,節點通過記錄父節點在數組里的索引號表示層級的等等,說到數組存放樹節點模式,我推薦阿信寫的那棵樹。在服務器端組織好腳本及節點字符串,客戶端瀏覽器加載這個頁面的時候順序執行其里面的腳本,將一個個節點做最初步的解析,然后再一個個地ADD到數組中去。這種流程看起來似乎沒有什么問題,在很多的傳統C/S結構編程中也就這種模式,但是隨著節點數的增多,這種流程在B/S里的缺點就出現了:腳本的執行效率不如強語言高!我做過測試,在5000節點的數據量時這一步操作將耗時1秒鐘,也許你認為這1秒鐘不算長,哪里有5000這么多節點的樹?但對于一個程序員來說,嚴謹是第一位的,若存在優化的可能性,情況出現的可能性,我們就得將它考慮到。那么有沒有可能再進一步優化呢?當然有,否則我就用不著這么大費口舌在這里講了。既然在腳本里執行 for 循環插入數組效率不高那我不用循環,且考慮到在數組里這么多節點中搜索我想要的某幾個節點,還得用循環,所以我干脆就不再用數組來存放樹節點了,那用什么呢?說到這里我得插幾句題外話,客戶端腳本也可以寫一些類,雖說寫出來的類沒有強語言那么好,繼承性不好等,但腳本終究還是可以寫出一些簡單的類的。類可以定義屬性,也可以定義方法,并且訪問屬性的時候可以通過屬性名下標直接訪問到屬性值,關于腳本里如何寫類,大家可以參考我在JavaScript和VBScript里對這方面的詳細說明。呵呵,說了這么多的題外話,不過相信大家也猜到了,新的節點存儲方式就是類的屬性自定義擴展方式。這種存儲方式隨著頁面的打開,頁面里的腳本被執行,類的屬性值不需要做任何添加的動作就直接寫到內存當中,比數組模式少去了ADD操作,這一步從字符串到內存的時間就是頁面打開的時間,我測試了一下,5000節點的樹用這種方式打開,只需0.1秒呀,與數組模式速度整整差了一個數量級呀!且這種模式還有一種好處,在幾千個節點里要找到目標節點,只需要知道節點對應的屬性名,一步就可以直接找到。
客戶端樹節點存儲模式說清楚了,那節點的快速檢索也就清楚了。已知節點名即在類里的屬性名的話,一步就找到了該節點,不過為了配合下一節的異步展示,我的檢索就沒有這么簡單了。試想在幾千或者幾萬個節點里要快速地找出符合條件的幾十個節點,這一步的耗時可想而知了,首先就得排除for循環法,for循環的效率在數組模式里大家就看到了,這一步操作特別在總節點數多的情況下就能讓使用者等瘋掉,顯然得另想辦法。于是我想到了正則表達式里的字符串模式匹配match(),我將所有的節點名(即類的屬性名)join()成一個大字符串,然后再用正則表達式匹配,一步就找出了想要的節點了。經測試,在三萬節點里找30個節點對象耗時小于0.1秒! - 異步展示 再來講一下節點字符串被解析之后轉化成HTML元素的這一步操作。上面已經有過一個計算,表達1000節點的樹的HTML字符量就有300K-600K之多,且這一步只能一個節點一個節點慢慢地生成,沒有什么取巧的辦法,想快點也只能是減小單個節點的HTML元素量罷,不過最快也得1-3秒每千節點呀,這也是沒有法子的事,誰叫DOM的效率不高呢!總得想個什么法子吧,否則象5000節點量的樹讓使用者等上個半分鐘一分鐘的,誰也受不了呀!因此我想出異步展示這招:頁面加載時并不立即生成所有節點的HTML元素,而是用戶展開多少節點就生成多少節點,節點的生成發生在用戶展開這個節點的時候。使用者在頁面打開的時候并不會立即把所有的節點都一次全部展開,而是一級級地往下展開的,就象你查看windows注冊表一樣,想看哪級才會去展開哪一級,這樣我就只需要在你展開那級的時候把這一級的節點轉化成HTML即可,每次轉化的節點只有幾十分甚至只有幾個而已,消除了使用者的等待時間。經過上面這幾個環節,終于一棵實用性,效率,擴展性俱佳的WEB TreeView出世了。
- 采用文字豎線 每個瀏覽器隨著使用客戶的不同,而總會產生不同的設置,其中有一項就是客戶端設置每次訪問都檢查網頁新版本,即客戶端不緩存。這個設置對一般的應用來說問題不會很大,但是對于使用圖片作為豎線的樹來說隨著節點總數的增多,圖片的使用量也就跟著巨量增加,可能會使用幾千甚至幾萬個小圖片,每張圖片是都很小,但量一大的話,將會嚴重影響樹的快速展示,因此針對這種情況就得換一種模式來展現了,那就是文字豎線,用文字加樣式就可以解決這個問題。我加了一個變量:MzTreeView.wordLine(布爾型,默認值為false),當網速過慢或者沒有使用本地緩存時這個屬性會自動設置成 true 而以文字代替圖片完成豎線(注:Opera 瀏覽器不支持文字豎線模式),當然你也可以一開始就強行設置值為 true,這樣樹就會始終用文字豎線了。
| MzTreeView.nodes | 集合 | 服務器端給樹指定數據源時數據存放的對象,具體存放格式如: MzTreeViewHandle.nodes["parentId_nodeId"] = "text: nodeText; icon: nodeIcon; url: nodeURL; ..."; |
| MzTreeView.url | 地址字符串 | 可讀寫,樹缺省的URL,默認值是 # |
| MzTreeView.target | 目標框架名 | 可讀寫,樹缺省的鏈接target,默認值是 _self |
| MzTreeView.name | 字符 | 只讀,樹的實例名,同樹實例化時作為參數傳入(大小寫敏感): var Tree = new MzTreeView("Tree"); |
| MzTreeView.currentNode | 樹節點 | 只讀,樹當前得到焦點的節點對象 |
| MzTreeView.icons | 集合 | 樹所使用的所有圖標存放 |
| MzTreeView.iconsExpand | 集合 | 樹里展開狀態的圖標存放 |
| MzTreeView.colors | 集合 | 樹里使用到的幾個顏色存放 |
MzTreeView 在客戶端的節點所擁有的屬性:
| node.id | 數字文本,節點的ID |
| node.parentId | 數字文本,節點對應的父節點ID |
| node.text | 文本,節點的顯示文本 |
| node.hint | 文本,節點的注釋說明 |
| node.icon | 文本,節點對應的圖標 |
| node.path | 文本,節點在樹里的絕對路徑:0_1_10_34 |
| node.url | 文本,該節點的 URL |
| node.target | 文本,該節點鏈接的目標框架名 |
| node.data | 文本,該節點所掛載的數據 |
| node.method | 文本,該節點的點擊對應處理語句 |
| node.parentNode | 對象,節點的父節點對象 |
| node.childNodes | 數組,包含節點下所有子節點的數組 |
| node.sourceIndex | 文本,服務器給予的數據里對象的 parentId_nodeId 的組合字符串 |
| node.hasChild | 布爾值,指該節點是否有子節點 |
| node.isLoad | 布爾值,本節點的子節點數據是否已經在客戶端初始化 |
| node.isExpand | 布爾值,節點的展開狀態 |
| MzTreeView.toString() | 類的默認初始運行 |
| MzTreeView.buildNode(id) | 將該節點的所有下級子節點轉換成 HTML 并在網頁上體現出來 |
| MzTreeView.nodeToHTML(node, AtEnd) | 將 node 轉換成 HTML |
| MzTreeView.load(id) | 從數據源里加載當前節點下的所有子節點 |
| MzTreeView.nodeInit(sourceIndex, parentId) | 節點的信息初始,從數據源到客戶端完整節點的轉化 |
| MzTreeView.focus(id) | 聚集到某個節點上 |
| MzTreeView.expand(id[, sureExpand]) | 展開節點(包含下級子節點數據的加載初始化) |
| MzTreeView.setIconPath(path) | 給節點圖片設置正確的路徑 |
| MzTreeView.nodeClick(id) | 節點鏈接點擊時同時被觸發的點擊事件處理方法 |
| MzTreeView.upperNode() | 跳轉到當前聚集節點的父級節點 |
| MzTreeView.lowerNode() | 跳轉到當前聚集節點的子級節點 |
| MzTreeView.pervNode() | 跳轉到當前聚集節點的上一節點 |
| MzTreeView.nextNode() | 跳轉到當前聚集節點的下一節點 |
| MzTreeView.expandAll() | 展開所有的樹點,在總節點量大于500時這步操作將會比較耗時 |
//MzTreeView1.0網頁樹類, 在實例化的時候請把實例名作參數傳遞進來
function MzTreeView(Tname)
{
if(typeof(Tname) != "string" || Tname == "")
throw(new Error(-1, '創建類實例的時候請把類實例的引用變量名傳遞進來!'));
//【property】
this.url = "#";
this.target = "_self";
this.name = Tname;
this.wordLine = false;
this.currentNode = null;
this.useArrow = true;
this.nodes = {};
this.node = {};
this.names = "";
this._d = "x0f";
this.index = 0;
this.divider = "_";
this.node["0"] =
{
"id": "0",
"path": "0",
"isLoad": false,
"childNodes": [],
"childAppend": "",
"sourceIndex": "0"
};
this.colors =
{
"highLight" : "#0A246A",
"highLightText" : "#FFFFFF",
"mouseOverBgColor" : "#D4D0C8"
};
this.icons = {
L0 : 'L0.gif', //┏
L1 : 'L1.gif', //┣
L2 : 'L2.gif', //┗
L3 : 'L3.gif', //━
L4 : 'L4.gif', //┃
PM0 : 'P0.gif', //+┏
PM1 : 'P1.gif', //+┣
PM2 : 'P2.gif', //+┗
PM3 : 'P3.gif', //+━
empty : 'L5.gif', //空白圖
root : 'root.gif', //缺省的根節點圖標
folder : 'folder.gif', //缺省的文件夾圖標
file : 'file.gif', //缺省的文件圖標
exit : 'exit.gif'
};
this.iconsExpand = { //存放節點圖片在展開時的對應圖片
PM0 : 'M0.gif', //-┏
PM1 : 'M1.gif', //-┣
PM2 : 'M2.gif', //-┗
PM3 : 'M3.gif', //-━
folder : 'folderopen.gif',
exit : 'exit.gif'
};
//擴展 document.getElementById(id) 多瀏覽器兼容性
//id 要查找的對象 id
this.getElementById = function(id)
{
if (typeof(id) != "string" || id == "") return null;
if (document.getElementById) return document.getElementById(id);
if (document.all) return document.all(id);
try {return eval(id);} catch(e){ return null;}
}
//MzTreeView 初始化入口函數
this.toString = function()
{
this.browserCheck();
this.dataFormat();
this.setStyle();
this.load("0");
var rootCN = this.node["0"].childNodes;
var str = "";
if(rootCN.length>0)
{
this.node["0"].hasChild = true;
for(var i=0; istr += this.nodeToHTML(rootCN[i], i==rootCN.length-1);
setTimeout(this.name +".expand('"+ rootCN[0].id +"', true); "+
this.name +".focusClientNode('"+ rootCN[0].id +"'); "+ this.name +".atRootIsEmpty()",10);
}
if (this.useArrow) //使用方向鍵控制跳轉到上級下級父級子級節點
{
if (document.attachEvent)
document.attachEvent("onkeydown", this.onkeydown);
else if (document.addEventListener)
document.addEventListener('keydown', this.onkeydown, false);
}
return ""οnclick='"+ this.name +".clickHandle(event)' "+
"οndblclick='"+ this.name +".dblClickHandle(event)' "+
">"+ str +"
轉載于:http://blog.itpub.net/7369349/viewspace-893534/
總結
以上是生活随笔為你收集整理的MzTreeView节点树(梅花雪)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 闭包的理解
- 下一篇: .NET 云原生架构师训练营(模块二 基