js基本树形插件实现
2017-01-31 00:39
337 查看
var Mock = [ {element:'轮播',id:'2',pid:'1'}, {element:'轮播小图',id:'3',pid:'2'}, {element:'首页',id:'1',pid:'0'}, {element:'轮播大图',id:'4',pid:'2'}, {element:'产品',id:'5',pid:'0'}, {element:'蔬菜',id:'6',pid:'5'}, {element:'有机蔬菜',id:'7',pid:'6'}, {element:'无机蔬菜',id:'8',pid:'6'}, {element:'水果',id:'9',pid:'5'}, {element:'有机水果',id:'10',pid:'9'}, {element:'有机大水果',id:'11',pid:'10'}, {element:'有机小水果',id:'12',pid:'10'}, {element:'无机水果',id:'13',pid:'9'}, ];
/** * @Author Toney * @Explain [树形插件] * @DateTime 2017-01-28 * @copyright [datacvg] * @param {[type]} root_id [description] */ var PTree = function(root_id){ var _this = this; //存放根据pid找到的节点 this.stack = []; //原始json数据 this.json = null; //存放json转化后的数据模型 this.nodes = []; this.root = document.getElementById(root_id); //字段 _this.keys = []; //缓存dom this.dom = {}; //获取最大json id this.getMaxId = function(){ var json = this.json.concat(); json.sort(function(a,b){ return b.id - a.id; }); return json[0]['id']; } /** * @Author Toney * @Explain [获取包含自身的子元素] * @DateTime 2017-01-29 * @copyright [datacvg] * @param {[type]} parent [description] * @return {[type]} [description] */ this.outChildrenNode = function(parent){ var result = [parent]; var childs = parent.getElementsByTagName('i'); for(var i = 0; i < childs.length; i++){ var curNode = childs[i]; if(curNode){ result.push(curNode); } } return result; } /** * @Author Toney * @Explain [按照json数据的key初始化一条空的json数据] * @DateTime 2017-01-29 * @copyright [datacvg] * @return {[type]} [description] */ this.initItem = function(){ var o = {}; while(_this.keys.length){ o[_this.keys.pop()] = null; } return o; } /** * @Author Toney * @Explain [视图转化为model] * @DateTime 2017-01-29 * @copyright [datacvg] * @param {[type]} nodes [description] * @return {[type]} [description] */ this.viewToModel = function(nodes){ var json = []; var temp = this.initItem(); for(var i = 0; i < nodes.length; i++){ var node = nodes[i]; var item = (function(){ var result = {}; for(key in temp){ result[key] = temp[key]; } return result; })(); for(var key in item){ item[key] = node.getAttribute(key); } json.push(item); } return json; } /** * @Author Toney * @Explain [更新model] * @DateTime 2017-01-28 * @copyright [datacvg] * @param {[string]} id [id主键] * @param {[string]} field [待更新的字段] * @param {[string]} val [修改后的值] */ this.updateModel = function(id,field,val){ for(var i = 0 ; i < this.json.length ; i++){ var item = this.json[i]; if(item['id'] == id){ this.json[i][field] = val; } } console.log(this.json); } this.addModel = function(item){ this.json.push(item); } this.removeModel = function(id){ for(var i = 0; i < _this.json.length;i++){ var item = _this.json[i]; if(item['id'] == id){ _this.json.splice(i,1); } } } //刷新视图 this.reRender = function(){ this.init(); this.removeAllNode(); this.initTree(this.json); } //初始化所有初始变量 this.init = function(){ //存放根据pid找到的节点 this.stack = []; //存放json转化后的数据模型 this.nodes = []; //缓存dom this.dom = {}; } //清空所有节点 this.removeAllNode = function(){ var childs=this.root.childNodes; for(var i=childs.length-1;i>=0;i--){ this.root.removeChild(childs.item(i)); } } //右键菜单 this.panelMenu = { dom:{ panel:null, }, pos:{ x:null, y:null }, list:[ {name:'添加',cmd:'add'}, {name:'删除',cmd:'remove'}, {name:'重命名',cmd:'rename'}, {name:'复制',cmd:'copy'}, {name:'粘贴',cmd:'paste'} ], strategy:{ dom:{ target:null }, json:{ copy:null }, add:function(item){ console.log('this is add'); //初始化一条数据 var _item = item || _this.initItem(); _item.id = _item.id || (parseInt(_this.getMaxId()) + 1).toString(); _item.pid = _item.pid || this.dom.target.getAttribute('id'); _item.element = _item.element || '新的节点'; _this.addModel(_item); _this.reRender(); this.dom.target = _this.findNode(_item.id); !item?this.rename():null; }, remove:function(){ console.log('this is remove'); _this.removeModel(this.dom.target.getAttribute('id')); _this.reRender(); }, rename:function(){ console.log('this is rename'); var input = { dom:{ input:null }, initInput:function(){ var input = document.createElement('input'); input.setAttribute('type','text'); input.setAttribute('size','10'); input.setAttribute('style','position: absolute;left: 0;top:0;height: 12px;'); this.dom.input = this.dom.input || input; this.listen({ onBlur:function(me,e){ var target = _this.panelMenu.strategy.dom.target; var rename = me.value; if(rename){ _this.updateModel(target.getAttribute('id'),'element',rename); _this.reRender(); } input.remove(); console.log('after rename',_this.json); } }); }, listen:function(config){ this.dom.input.addEventListener('blur',function(e){ e?e.stopPropagation():window.event.cancelBubble = true; config.onBlur(this,e); }); this.dom.input.addEventListener('click',function(e){ e?e.stopPropagation():window.event.cancelBubble = true; }); }, 4000 appendTo:function(parent){ _this.add(this.dom.input,parent); }, remove:function(){ if(this.dom.input && this.dom.input.parentNode){ this.dom.input.parentNode.removeChild(this.dom.input); } }, focus:function(){ this.dom.input.focus(); } }; input.initInput(); input.appendTo(this.dom.target); input.focus(); }, copy:function(){ console.log('this is copy'); this.dom.copy = this.dom.copy || this.dom.target; var outChildren = _this.outChildrenNode(this.dom.target); var model = _this.viewToModel(outChildren); this.json.copy = model; }, paste:function(){ console.log('this is paste'); var origin_id = this.json.copy[0]['id']; var max_id = parseInt(_this.getMaxId()) + 1; var minus = max_id - parseInt(origin_id); for(var i = 0; i < this.json.copy.length; i++){ this.json.copy[i]['id'] = parseInt(this.json.copy[i]['id']) + minus; this.json.copy[i]['pid'] = parseInt(this.json.copy[i]['pid']) + minus; } this.json.copy[0]['pid'] = this.dom.target.getAttribute('id'); _this.json = _this.json.concat(this.json.copy); _this.reRender(); } }, initPanel:function(){ if(!this.dom.panel){ var __this = this; this.dom.panel = (function(){ var panel = document.createElement('div'); var style = "background:black;opacity:0.9;color:white;cursor:pointer;text-align:center;background:gray;width:200px;height:auto;position:fixed;left:10px;top:10px"; panel.setAttribute('style',style); //菜单项 (function(){ for(var i = 0; i < __this.list.length;i++){ var item = __this.list[i]; var p = document.createElement('p'); p.innerHTML = item['name']; panel.appendChild(p); (function(p,item){ p.addEventListener('click',function(e){ __this.strategy[item['cmd']](); }); })(p,item); } })(); return panel; })(); } }, open:function(e){ //初始化一个菜单面板 this.initPanel(); //设置初始位置为光标位置 this.dom.panel.style.left = e.clientX+'px'; this.dom.panel.style.top = e.clientY+'px'; }, close:function(){ if(this.dom.panel && this.dom.panel.parentNode){ this.dom.panel.parentNode.removeChild(this.dom.panel); } } }; /** * @Author Toney * @Explain [根据一项json数据创建一个节点,key->value设置为节点的属性] * @DateTime 2017-01-24 * @copyright [datacvg] * @param {[type]} item [description] * @return {[type]} [description] */ this.createNode = function(item){ var node = document.createElement('i'); node.innerHTML = item['element']; for(var key in item){ node.setAttribute(key,item[key]); } //其他属性 node.setAttribute('open','yes'); node.setAttribute('class','icon-minus'); return node; } /** * @Author Toney * @Explain [description] * @DateTime 2017-01-24 * @copyright [datacvg] * @param {[type]} child [description] * @param {[type]} parent [description] */ this.add = function(child,parent){ parent.appendChild(child); } /** * @Author Toney * @Explain [入栈根据pid找到的节点] * @DateTime 2017-01-24 * @copyright [datacvg] * @param {[type]} pid [description] * @return {[type]} [description] */ this.pushFoundChild = function(pid){ for(var i = 0; i < this.nodes.length; i++){ var node = this.nodes[i]; if(node.getAttribute('pid') == pid){ this.stack.push(node); } } return this.stack.length; } /** * @Author Toney * @Explain [获取当前节点的父节点] * @DateTime 2017-01-24 * @copyright [datacvg] * @param {[type]} pid [description] * @return {[type]} [description] */ this.findParent = function(pid){ if(pid == 0){ return this.root; } for(var i = 0; i < this.nodes.length; i++){ var node = this.nodes[i]; if(node.getAttribute('id') == pid){ return node; } } return null; } /** * @Author Toney * @Explain [获取某个节点] * @DateTime 2017-01-29 * @copyright [datacvg] * @param {[type]} id [description] * @return {[type]} [description] */ this.findNode = function(id){ for(var i = 0; i < this.nodes.length; i++){ var node = this.nodes[i]; if(node.getAttribute('id') == id){ return node; } } return null; } /** * @Author Toney * @Explain [把json数据转化为一个个节点] * @DateTime 2017-01-24 * @copyright [datacvg] * @param {[type]} json [description] * @return {[type]} [description] */ this.initNodes = function(json){ if(json.length > 0){ for(var i = 0 ; i < json.length ; i++){ var item = json[i]; var node = this.createNode(item); this.nodes.push(node); } }else{ throw '传入的Json串不合法'; } }; /** * @Author Toney * @Explain [监听所有I节点] * @DateTime 2017-01-24 * @copyright [datacvg] * @param {[type]} config [description] * @return {[type]} [description] */ this.listenI = function(config){ var cfg = { onclick:function(){}, onMouseDown:function(){} }; //深度继承 for(var key in config){ cfg[key] = config[key]; } this.dom.i = this.dom.i || this.root.getElementsByTagName('i'); for(var i = 0; i < this.dom.i.length; i++){ var item = this.dom.i[i]; (function(item){ item.addEventListener('click',function(e){ e?e.stopPropagation():window.event.cancelBubble = true; cfg.onclick(this,e); }); item.addEventListener('mousedown',function(e){ e?e.stopPropagation():window.event.cancelBubble = true; cfg.onMouseDown(this,e); }); })(item); } } this.openMenu = function(e){ this.panelMenu.open(e); this.root.appendChild(this.panelMenu.dom.panel); }, /** * @Author Toney * @Explain [判断某个节点是否为展开状态] * @DateTime 2017-01-24 * @copyright [datacvg] * @param {[type]} node [description] * @return {Boolean} [description] */ this.isOpen = function(node){ if(node.getAttribute('open') == 'yes'){ return true; }else{ return false; } } /** * @Author Toney * @Explain [展开一个节点] * @DateTime 2017-01-24 * @copyright [datacvg] * @return {[type]} [description] */ this.openNode = function(node){ //设置状态为展开 node.setAttribute('open','yes'); //设置图标为减号 node.setAttribute('class','icon-minus'); var children = node.getElementsByTagName('i'); for(var i = 0; i < children.length;i++){ var item = children[i]; item.style.display = 'block'; } } /** * @Author Toney * @Explain [折叠一个节点] * @DateTime 2017-01-24 * @copyright [datacvg] * @return {[type]} [description] */ this.closeNode = function(node){ //设置状态为关闭 node.setAttribute('open','no'); //设置图标为加号 node.setAttribute('class','icon-add'); var children = node.getElementsByTagName('i'); for(var i = 0; i < children.length;i++){ var item = children[i]; item.style.display = 'none'; } } this.listenRoot = function(){ this.root.addEventListener('click',function(e){ var target = e.target; if(target != 'p'){ _this.panelMenu.close(); } }); } this.initListen = function(){ this.listenI({ onclick:function(me){ if(_this.isOpen(me)){ _this.closeNode(me); }else{ _this.openNode(me); } }, onMouseDown:function(me,e){ _this.panelMenu.strategy.dom.target = me; switch(e.button){ case 2: _this.openMenu(e); break; } } }); this.listenRoot(); } this.initKeys = function(json){ for(var key in json[0]){ this.keys.push(key); } } this.testMock = function(num){ var start = new Date().getTime();//起始时间 for(var i = 0; i < num; i++){ var item = { pid:0, id:null, element:'dskljfkdsl' }; this.json.push(item); } this.reRender(); var end = new Date().getTime();//接受时间 return (end - start)+"ms";//返回函数执行需要时间 } //禁止浏览器默认鼠标右键 this.forbidBrowserMouseRightKey = function(){ window.oncontextmenu = function(){ window.event.returnValue=false; return false; } } }; /** * @Author Toney * @Explain [把节点拼成树] * @DateTime 2017-01-24 * @copyright [datacvg] * @return {[type]} [description] */ PTree.prototype.initTree = function(json){ this.json = json; this.initNodes(json); this.initKeys(json); var pid = 0, curNode = null, parent = null; console.log(this.nodes); while(this.pushFoundChild(pid)){ curNode = this.stack.pop(); parent = this.findParent(curNode.getAttribute('pid')); this.add(curNode,parent); pid = curNode.getAttribute('id'); } this.initListen(); this.forbidBrowserMouseRightKey(); } var pTree = new PTree('panel-tree'); pTree.initTree(Mock);
.p-tree { width: 200px; height: 300px; background: #ddd; resize: both; overflow: auto; padding: 20px; } .p-tree i { display: block; font-style: normal; cursor: pointer; color: black; position: relative; margin-left: 20px; } .p-tree i.selected { color: red; } .p-tree i.icon-add:before { content: '+'; } .p-tree i.icon-minus:before { content: '-'; }
相关文章推荐
- 轻松学习jQuery插件EasyUI EasyUI实现树形网络基本操作(2)
- 轻松学习jQuery插件EasyUI EasyUI实现树形网络基本操作(2)
- [学习笔记]zTree是一个很好的js插件实现加载树形结构
- js的树形结构怎么实现
- nginx做反向代理,实现负载均衡基本配置。无法加载css,js或图片
- 通过继承IHttpHandle实现JS插件的组织与管理
- Applet项目整合插件Flat Jar--实现JS调用Applet
- JS实现树形菜单
- 关于级联(树形权限)的基本实现思路
- js实现树形菜单效果
- 树形checkbox的选择(用JS实现)
- js Tree - 树形菜单插件
- DIV+Js实现简单树形菜单
- 使用ajaxFileUpload.js插件实现ajax无刷新上传文件
- JavaScript 实现的滚动条,熟悉一下JS的基本语法一些细节建议查看手册:
- 用JS实现一个TreeMenu树形菜单效果
- 用js实现树形结构
- 不间断滚动JS打包类,基本可以实现所有的滚动效果,太强了
- Jquery.LazyLoad.js修正版下载,实现图片延迟加载插件
- JS简单实现文件上传(无需插件)