ztree高级实例(原创)
最近项目中需要设计一个类似收藏夹的功能保存用户常用的东西,需要客户体验好,所以想到了以前用过的ztree。
在用ztree的过程中遇到一些问题,在此记录,提醒自己,也帮助遇到相同情况的同行们。
1.至少在版本3.5.14中exedit和exhide两个js还是有冲突的。表现为拖拽节点到新建的节点下时无法获取目标节点tId,但是专门用方法获取tId却能成功获取。所以在demo中只好用禁止拖拽和字体颜色变黑代替节点隐藏。
2.变成异步读取保存数据后,跟提供的demo有较大不同,因为我需要实时更新数据,即每步增删改都需要异步处理,而不是退出时才保存数据。
新建节点异步保存问题:
一开始的设计是,新建节点后立即进入编辑状态,完成编辑前保存数据,但发现存在2个问题:
一是用户新建节点后可以按ESC退出编辑状态,二是新建节点较快时偶尔不能进入编辑状态的情况。
于是改成新建节点的时候是先增加节点,成功后再异步保存,但这样有时节点增加了,数据却没成功保存,
之后,又改成了先保存数据,成功后增加节点。
但又出现另一个问题,有时新建的节点变成了双份。加一次竟然会出来2个。
分析原因,感觉是父级节点加载或新建后还没展开过,增加子级节点时会异步加载数据,刚好这时已经插入了一条,而前端也会生成一个。
这个问题还没有完美解决,暂时通过首次加载时全部展开,新建父节点时,成功添加子级节点后父级节点和子级节点都展开,这样不会出现2个节点的情况。
基于父节点第一次展开时才加载数据的推测,拖拽出现2个节点的情况也是这样处理的。
拖拽节点异步保存问题:(以下提到的加载指父节点加载子节点)
经过测试,发现在树加载完成后全部展开节点,只会对有子节点的父节点进行加载。即有子节点的父节点的zAsync属性为true,无子节点的父节点的zAsync==false,因此在拖拽节点到无子节点的父节点时会进行加载。
在我测试实例时,拖拽节点有时会出现重复,节点变2份,这是因为我把保存节点的代码放到了BeforeDrop中。如果拖到未展开的目标节点马上放下,就会出现双份,节点加载会有一点延时,如果很快将节点放下,那么数据已经保存到数据库,那么加载的和ztree创建的就是双份。如果拖到未展开的目标节点停一会在放下就不会出现双份,当拖拽节点到目标节点停住的时候会展开该节点同时触发目标节点的加载,在加载时还未保存数据,因此不会出现重复的情况。而如果拖到已展开的无子节点的父节点时,总是会出现双份。
但是我们不可能要求用户,“拖拽的时候慢点”,所以这个问题需要解决。
目前找到了2种解决办法:
1.用reAsyncChildNodes强制加载。在在树加载完成并全部展开父节点后,判断所有父节点的zAsync属性,为false则强制加载该节点。这样拖拽时都不会再加载了。
2.把保存节点的代码放到了onDrop中。也就是拖拽节点成功后,保存数据,如果保存不成功还可以调用remove方法删除该节点。
1 <!DOCTYPE html> 2 <HTML> 3 <HEAD> 4 <TITLE> ZTREE DEMO - edit</TITLE> 5 <meta http-equiv="content-type" content="text/html; charset=UTF-8"> 6 <link rel="stylesheet" href="../../../css/demo.css" type="text/css"> 7 <link rel="stylesheet" href="../../../css/zTreeStyle/zTreeStyle.css" type="text/css"> 8 <script type="text/javascript" src="../../../js/jquery-1.4.4.min.js"></script> 9 <script type="text/javascript" src="../../../js/jquery.ztree.all-3.5.min.js"></script> 10 <script type="text/javascript" src="../../../js/jquery.ztree.exhide-3.5.min.js"></script> 11 <SCRIPT type="text/javascript"> 12 <!-- 13 var setting = { 14 15 view : { 16 fontCss: {}, 17 addHoverDom: addHoverDom, 18 removeHoverDom: removeHoverDom, 19 selectedMulti: true 20 }, 21 edit: { 22 enable: true, 23 editNameSelectAll: true, 24 removeTitle : "删除", 25 renameTitle : "编辑", 26 showRemoveBtn: true, 27 showRenameBtn: showRenameBtn, 28 drag: { 29 isCopy: false, 30 isMove: true, 31 prev: canPrevAndNext, 32 next: canPrevAndNext, 33 inner: true 34 } 35 }, 36 data: { 37 keep: { 38 leaf: true, 39 parent: true 40 }, 41 simpleData: { 42 enable: true 43 } 44 }, 45 callback: { 46 //beforeDrag: beforeDrag 47 beforeRemove: zTreeBeforeRemove, 48 beforeRename: zTreeBeforeRename, 49 beforeDrop: zTreeBeforeDrop 50 } 51 }; 52 var setting2 = { 53 54 view : { 55 fontCss: setFontCss, 56 dblClickExpand : true, 57 selectedMulti: true, 58 expandSpeed: "fast" 59 }, 60 61 edit: { 62 enable: true, 63 drag: { 64 isCopy: true, 65 isMove: false, 66 prev: false, 67 next: false, 68 inner: false 69 }, 70 showRemoveBtn: false, 71 showRenameBtn: false 72 }, 73 data : { 74 keep: { 75 leaf: true, 76 parent: true 77 }, 78 simpleData : { 79 enable : true 80 } 81 }, 82 callback : { 83 beforeDrag: beforeDrag, 84 beforeDrop: zTreeBeforeDrop 85 86 } 87 }; 88 89 var zNodes =[ 90 { id:1, pId:0, name:"父节点 1", open:true}, 91 { id:11, pId:1, name:"叶子节点 1-1"}, 92 { id:12, pId:1, name:"叶子节点 1-2"}, 93 { id:2, pId:0, name:"父节点 2", open:true}, 94 { id:21, pId:2, name:"叶子节点 2-1"}, 95 { id:23, pId:2, name:"叶子节点 2-3"}, 96 { id:3, pId:0, name:"父节点 3", open:true}, 97 { id:32, pId:3, name:"叶子节点 3-2"}, 98 { id:33, pId:3, name:"叶子节点 3-3"} 99 ]; 100 var zNodes2 =[ 101 { id:1, pId:0, name:"父节点 1", open:true, drag:false}, 102 { id:11, pId:1, name:"叶子节点 1-1"}, 103 { id:12, pId:1, name:"叶子节点 1-2"}, 104 { id:13, pId:1, name:"叶子节点 1-3"}, 105 { id:14, pId:1, name:"父节点 14", open:true, drag:false}, 106 { id:141, pId:14, name:"叶子节点 14-1"}, 107 { id:142, pId:14, name:"叶子节点 14-2"}, 108 { id:143, pId:14, name:"叶子节点 14-3"}, 109 { id:2, pId:0, name:"父节点 2", open:true, drag:false}, 110 { id:21, pId:2, name:"叶子节点 2-1"}, 111 { id:22, pId:2, name:"叶子节点 2-2"}, 112 { id:23, pId:2, name:"叶子节点 2-3"}, 113 { id:3, pId:0, name:"父节点 3", open:true, drag:false}, 114 { id:31, pId:3, name:"叶子节点 3-1"}, 115 { id:32, pId:3, name:"叶子节点 3-2"}, 116 { id:33, pId:3, name:"叶子节点 3-3"} 117 ]; 118 function canPrevAndNext(treeId, nodes, targetNode) { 119 return !targetNode.isParent; 120 } 121 122 //用于捕获节点被拖拽之前的事件回调函数,并且根据返回值确定是否允许开启拖拽操作 123 function beforeDrag(treeId, treeNodes) { 124 for (var i=0,l=treeNodes.length; i<l; i++) { 125 if (treeNodes[i].drag === false) { 126 return false; 127 } else if (treeNodes[i].parentTId && treeNodes[i].getParentNode().childDrag === false) { 128 return false; 129 } 130 } 131 return true; 132 } 133 //节点后的新建文件夹图标方法 134 var newCount = 1; 135 function addHoverDom(treeId, treeNode) { 136 var sObj = $("#" + treeNode.tId + "_span"); 137 if (!treeNode.isParent || treeNode.editNameFlag || $("#addBtn_"+treeNode.tId).length>0) return; 138 var addStr = "<span class='button add' id='addBtn_" + treeNode.tId + "' title='新建文件夹' onfocus='this.blur();'></span>"; 139 sObj.after(addStr); 140 var btn = $("#addBtn_"+treeNode.tId); 141 if (btn) btn.bind("click", function(){ 142 var zTree = $.fn.zTree.getZTreeObj(treeId); 143 var currentTime = GetCurrentTime(); 144 treeNode = zTree.addNodes(treeNode, {id:currentTime, pId:treeNode.id, isParent:true, name:"新建文件夹" + (newCount++)}); 145 if (treeNode) { 146 zTree.editName(treeNode[0]); 147 } else { 148 alert("新建文件夹失败!请稍后再试"); 149 } 150 }); 151 }; 152 function removeHoverDom(treeId, treeNode) { 153 $("#addBtn_"+treeNode.tId).unbind().remove(); 154 }; 155 function showRenameBtn(treeId, treeNode) { 156 return treeNode.isParent; 157 } 158 function updateNodes(nodeList) { 159 var zTree = $.fn.zTree.getZTreeObj("treeDemo2"); 160 for( var i=0; i<nodeList.length; i++) { 161 nodeList[i].drag = (nodeList[i].drag == null) ? false: !nodeList[i].drag; 162 zTree.updateNode(nodeList[i]); 163 } 164 } 165 //个性化样式 166 function setFontCss(treeId, treeNode) { 167 return (treeNode.isParent == false && treeNode.drag == false) ? {color:"#d0d0d0", "font-weight":"bold"} : {color:"", "font-weight":""}; 168 } 169 //用于捕获节点被删除之前的事件回调函数,并且根据返回值确定是否允许删除操作 170 function zTreeBeforeRemove(treeId, treeNode) { 171 if(!confirm(" 确认删除\""+treeNode.name +"\"及其子节点?")){ 172 return false; 173 } 174 changNodesStat(treeNode); 175 return true; 176 } 177 //用于捕获节点编辑名称结束(Input 失去焦点 或 按下 Enter 键)之后,更新节点名称数据之前的事件回调函数,并且根据返回值确定是否允许更改名称的操作 178 function zTreeBeforeRename(treeId, treeNode, newName, isCancel) { 179 if (newName.length == 0) { 180 alert("名称不能为空!"); 181 var zTree = $.fn.zTree.getZTreeObj(treeId); 182 setTimeout(function(){zTree.editName(treeNode);}, 10); 183 return false; 184 } 185 } 186 //用于捕获节点拖拽操作结束之前的事件回调函数,并且根据返回值确定是否允许此拖拽操作 187 function zTreeBeforeDrop(treeId, treeNodes, targetNode, moveType) { 188 updateNodes(treeNodes); 189 } 190 //“新建文件夹”按钮的方法 191 function add(e) { 192 var zTree = $.fn.zTree.getZTreeObj("treeDemo"), 193 isParent = e.data.isParent, 194 nodes = zTree.getSelectedNodes(), 195 treeNode = nodes[0]; 196 if (treeNode && !treeNode.isParent == true) { 197 alert("只能在文件夹或根目录下新建文件夹!"); 198 return; 199 } 200 var pId = (treeNode) ? treeNode.id : "0"; 201 var currentTime = GetCurrentTime(); 202 treeNode = zTree.addNodes(treeNode, {id:currentTime, pId:pId, isParent:isParent, name:"新建文件夹" + (newCount++)}); 203 if (treeNode) { 204 zTree.editName(treeNode[0]); 205 } else { 206 alert("新建文件夹失败!请稍后再试"); 207 } 208 }; 209 function GetCurrentTime() { 210 var myDate = new Date(); 211 var year = myDate.getFullYear(); 212 var month = parseInt(myDate.getMonth().toString()) + 1; //month是从0开始计数的,因此要 + 1 213 var date = myDate.getDate(); 214 var hour = myDate.getHours(); 215 var minute = myDate.getMinutes(); 216 var second = myDate.getSeconds(); 217 var millisecond = myDate.getMilliseconds(); 218 if (month < 10) month = "0" + month.toString(); 219 if (date < 10) date = "0" + date.toString(); 220 if (hour < 10) hour = "0" + hour.toString(); 221 if (minute < 10) minute = "0" + minute.toString(); 222 if (second < 10) second = "0" + second.toString(); 223 if(millisecond < 10){ 224 millisecond = "00" + millisecond.toString(); 225 }else if(millisecond < 100){ 226 millisecond = "0" + millisecond.toString(); 227 } 228 var currentTime = year.toString() + month.toString() + date.toString() + hour.toString() + minute.toString() + second.toString() + millisecond.toString(); //返回时间的数字组合 229 return currentTime; 230 } 231 function changNodesStat(treeNode){ 232 if(treeNode && treeNode.isParent === false){ 233 var zTree2 = $.fn.zTree.getZTreeObj("treeDemo2"); 234 var node = zTree2.getNodeByParam("id", treeNode.id); 235 if(node) updateNodes([node]); 236 }else { 237 var nodes = filterNodes(treeNode); 238 updateNodes(nodes); 239 } 240 } 241 //自定义过滤器,传入treeDemo的父节点treeNode,返回treeDemo2中匹配id与treeNode下的叶子节点相同的节点 242 function filterNodes(treeNode){ 243 var zTree2 = $.fn.zTree.getZTreeObj("treeDemo2"); 244 var nodes = zTree2.getNodesByFilter(filter,false,null,treeNode); 245 return nodes; 246 } 247 function filter(node,treeNode) { 248 var flag = false; 249 var zTree = $.fn.zTree.getZTreeObj("treeDemo"); 250 var nodes = zTree.getNodesByParam("isParent", false , treeNode); 251 for ( var i = 0; i < nodes.length; i++) { 252 flag = (nodes[i].id == node.id); 253 if(flag)break; 254 } 255 return flag; 256 } 257 258 $(document).ready(function(){ 259 $.fn.zTree.init($("#treeDemo"), setting, zNodes); 260 $.fn.zTree.init($("#treeDemo2"), setting2, zNodes2); 261 $("#addParent").bind("click", {isParent:true}, add); 262 changNodesStat(); 263 264 //获取全部节点 265 //var zTree2 = $.fn.zTree.getZTreeObj("treeDemo2"); 266 //var nodes = zTree2.getNodesByParam("null", null , null); 267 //alert(nodes.length); 268 }); 269 //--> 270 </SCRIPT> 271 <style type="text/css"> 272 .ztree li span.button.add {margin-left:2px; margin-right: -1px; background-position:-144px 0; vertical-align:top; *vertical-align:middle} 273 </style> 274 </HEAD> 275 276 <BODY> 277 278 <div class="content_wrap"> 279 280 <div class="zTreeDemoBackground left"> 281 常用指标:<input type="button" id="addParent" value="新建文件夹" onclick="return false;"> 282 <ul id="treeDemo" class="ztree"></ul> 283 </div> 284 285 <div class="zTreeDemoBackground right"> 286 全量指标: 287 <ul id="treeDemo2" class="ztree"></ul> 288 </div> 289 </div> 290 291 </BODY> 292 </HTML>
说明:里面需要的js和css都可以在ztree官网找到
posted on 2013-08-11 00:56 guodefu909 阅读(2378) 评论(0) 编辑 收藏 举报