JavaScript 树

来源:互联网 发布:单片机流程图是啥意思 编辑:程序博客网 时间:2024/06/13 10:44

最近公司事情比较少, 想着前一段时间公司用的 DOM树结构,就想自己写一个就当练练手了(在此发表出来仅供交流,不好的地方望指教),好了开始吧

DOM树结构大同小异,附上写好的效果图一张 :

嗯,这就是效果图 、实用而简约(貌似有点太简约了、后面再调啦):

写这个我首先想到的是html 怎么布局: 这个问题纠结了半天(主要是想后面怎么用js来更好的控制,我不想等写好之后 每一层目录  我都加一个className 那样不忍直视):代码如下:

<div class="s-tree"><!-- 一级目录 --><div class="tree-list-box line-box line-box-f"><span class="tree-list tree-list1"><!-- 选择框 --><span class="tree-select"></span><!-- 展示的信息 --><span class="tree-info">一级目录</span></span><!-- 二级目录 --><div class="tree-list-box line-box"><span class="tree-list tree-list1"><!-- 选择框 --><span class="tree-select"></span><!-- 展示的信息 --><span class="tree-info">二级目录</span></span><!-- 三级目录 --><div class="tree-list-box line-box"><span class="tree-list tree-list1"><!-- 选择框 --><span class="tree-select"></span><!-- 展示的信息 --><span class="tree-info">三级目录</span></span><!-- 。。。。一次往下加 --></div></div></div></div>

顺便附上css ( css是用sass写的 ):

.s-tree{width: 500px;overflow: hidden;margin: 100px auto 0px;border: 1px solid #ccc;@include br(5px);padding: 10px;position: relative;}.tree-list-box{width: 100%;font-size: 14px;overflow: hidden;color: #505050;position: relative;margin-left: 0.5em;padding-left: 1.0em;}.tree-list{display: inline-block;line-height: 1em;height: 1em;cursor: pointer;position: relative;&:after,&:before{content: '';display: block;position: absolute;}&:after{width: 1em;border-top: 1px dashed #ccc;top: 50%;left: -1em;}&.active{.tree-select{&:after{content: "\2714";margin-left: 1px;}}}}.tree-list1{&:after,&:before{display: none;}}.line-box{&:before{content: '';display: block;position: absolute;left: 1.5em;top: 1em;height: 100%;width: 1px;border-left: 1px dashed #ccc;}}.line-box-f{margin-left: 0px;}.tree-select,.tree-info{float: left;}.tree-select{line-height: 1;@include wh(1em);position: relative;top: 50%;margin-top: -0.5em;border: 1px solid #ccc;&:hover{border-color: #999;}}.tree-info{padding-left: 0.5em;&:hover{color: red;}}.s-tree-search{width: 80%;height: 52px;line-height: 52px;padding-left:15px;font-size: 18px;margin: 0 auto;}

ps: 我用的sublime  装的 插件 保存之后 自动 编译压缩成 css 文件  很不错 推荐 安装文档  大家可以参考这里


嗯,布局写好之后  下面就开始 javascript了 (不怎么会讲、后面上完整代码,需要的小伙伴可以看看),我们以插件的形式(形如) :

;(function(win,doc){var Stree;Stree = (function(win,doc){function Stree(ops){this._pNode = ops['el'] || '',//数据树的盒子domthis._sNode = ops['search'] || '',//搜索框this._data = ops['data'] || '',//原始数据this._fData = [],//格式化的数据this._bindFunc = {},//绑定事件的方法集合this._doms = {},//储存创建好的dom节点this._searchTimer = null,this._groups = [],//储存数据中的group属性this.init();}return Stree.prototype.init = function(){//code},Stree;})(win,doc),win.Stree = Stree;})(window,document) 后面我们调用的时候直接 new Stree(我们的参数)  然后在Stree的原型上添加方法 就可以了 (这是结构 后面有完整代码)

完整css:

*{margin:0;padding:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}body,html{width:100%;overflow-x:hidden;color:#4d4d4d;font:14px/1.5 "Helvetica Neue",Helvetica,Arial,"Microsoft Yahei","Hiragino Sans GB","Heiti SC","WenQuanYi Micro Hei",sans-serif}body{-webkit-tap-highlight-color:rgba(255,0,0,0.5);-webkit-font-smoothing:antialiased;background-color:#fff}a{text-decoration:none}img{display:block;width:100%;vertical-align:middle}ul,li{list-style:none}.main{width:100%;overflow:hidden}.s-tree{width:500px;overflow:hidden;margin:100px auto 0px;border:1px solid #ccc;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;padding:10px;position:relative}.tree-list-box{width:100%;font-size:14px;overflow:hidden;color:#505050;position:relative;margin-left:0.5em;padding-left:1.0em}.tree-list{display:inline-block;line-height:1em;height:1em;cursor:pointer;position:relative}.tree-list:after,.tree-list:before{content:'';display:block;position:absolute}.tree-list:after{width:1em;border-top:1px dashed #ccc;top:50%;left:-1em}.tree-list.active .tree-select:after{content:"\2714";margin-left:1px}.tree-list1:after,.tree-list1:before{display:none}.line-box:before{content:'';display:block;position:absolute;left:1.5em;top:1em;height:100%;width:1px;border-left:1px dashed #ccc}.line-box-f{margin-left:0px}.tree-select,.tree-info{float:left}.tree-select{line-height:1;height:1em;width:1em;position:relative;top:50%;margin-top:-0.5em;border:1px solid #ccc}.tree-select:hover{border-color:#999}.tree-info{padding-left:0.5em}.tree-info:hover{color:red}.s-tree-search{width:80%;height:52px;line-height:52px;padding-left:15px;font-size:18px;margin:0 auto}/*# sourceMappingURL=stree.css.map */



完整js:

;(function(win,doc){var Stree;Stree = (function(win,doc){function Stree(ops){this._pNode = ops['el'] || '',//数据树的盒子domthis._sNode = ops['search'] || '',//搜索框this._data = ops['data'] || '',//原始数据this._fData = [],//格式化的数据this._bindFunc = {},//绑定事件的方法集合this._doms = {},//储存创建好的dom节点this._searchTimer = null,this._groups = [],//储存数据中的group属性this.init();}return Stree.prototype.init = function(){var _pNode = this._pNode,_d = this._data,_len = _d.length;if(!_pNode || !_d || !_len) throw new Error("el || data can not empty");if( _pNode.nodeType != 1 ) throw new Error("el is not a dom");/** * 在这里  首先要解析数据 判断层级结构  这个结构 不同项目可能会有不同的 形式  * * 我们把不同的形式 转换成我们要的结构 */this.formatDateHandler(this._data); //格式化传入的数据/** * 初始化的时候  默认 不展开 */this.treeInfoHandler(this,null);/** * 初始化 搜索功能 */if( !!this._sNode ){this.addEvent(this._sNode,"change",this.wordSearchHandler,false);}},Stree.prototype.wordSearchHandler = function(_this,el){clearTimeout(_this._searchTimer);_this._searchTimer = setTimeout(function(){var _doms = _this._doms,_pNode = _this._pNode,_v = el.value;_pNode.innerHTML = '';if(!_v){_this.formatDateHandler(_this._data,true);return false;}for(let key in _doms){if(_doms[key].innerHTML.indexOf(_v) != -1){if(_doms[key].firstChild.innerHTML.indexOf(_v) != -1){var _cl_name = _doms[key].className,_fc_node = _doms[key].firstChild,_fc_cl = _fc_node.className;if( _cl_name.indexOf("line-box-f") == -1){_cl_name += " line-box-f";_doms[key].className = _cl_name;}if( _fc_cl.indexOf("tree-list1") == -1){_fc_cl += " tree-list1";_fc_node.className = _fc_cl;}_pNode.appendChild(_doms[key]);}}}if(!_pNode.innerHTML){_pNode.innerHTML = "No Reault";}},300)},Stree.prototype.parentSelectedHandler = function(_dom,type,flag){/** * 实现 这里我们 要把点击元素的  父元素 都打上勾  要一层一层的找了 * * type 作用  当我们取消时候  检查 兄弟节点 还有没有选中的  如果没有  false  如果有 true 阻止取消操作 * * flag  作用  是进行 选中还是取消操作 */if(flag&&type) return false;if( _dom.className.indexOf("line-box-f") != -1 ) return false;var _el = _dom.parentNode.firstChild,_cl_name = _el.className;if(flag){if( _cl_name.indexOf("active") != -1 ){_el.className = _cl_name.replace(" active", '');}}else{if( _cl_name.indexOf("active") == -1 ){_cl_name += " active";_el.className = _cl_name;}}/** * 找啊找 */arguments.callee.call(this,_dom.parentNode,this.siblingsIsSelected(_dom.parentNode.parentNode.childNodes),flag);},Stree.prototype.siblingsIsSelected = function(nodes){if( !nodes ) return false;for(let i=1,len=nodes.length;i<len;i++){if(nodes[i].firstChild.className.indexOf("active") != -1) return true;}return false; },Stree.prototype.treeSelectHandler = function(_this,el){var _elp = el.parentNode,_elps = _elp.parentNode,//tree-list-box_trees = _elps.querySelectorAll(".tree-list"),_cl_name = _elp.className,flag,opflag,_sNode;// console.log(_elps.parentNode);if( _cl_name.indexOf("active") != -1 ){_elp.className = _cl_name.replace(" active", '');flag = true;}else{_cl_name += " active";_elp.className = _cl_name;flag = false;}_this.parentSelectedHandler( _elps, _this.siblingsIsSelected(_elps.parentNode.childNodes), flag );Array.prototype.forEach.call(_trees,function(item){if(item === _elp) return false;_cl_name = item.className;if(flag){item.className = _cl_name.replace(" active", '');}else{_cl_name += " active";item.className = _cl_name;}})},Stree.prototype.createDomHandler = function(_d,type){/** * 递归 */if( Object.prototype.toString.call(_d).slice(8, -1) == "Array" ){for( let i=0,len=_d.length;i<len;i++){arguments.callee.call(this,_d[i],type);}}else{if(type){/** * 具体dom 合并操作 */this.showDomsHandler( _d );}else{this.createTreeBoxDom(_d);}if( _d["info"] ) {this._groups.push(_d["group"]);arguments.callee.call(this,_d["info"],type);}}},Stree.prototype.formatDateHandler = function(_d,type){if( !_d ) return false;for(let i=0,_len = _d.length; i<_len;i++){/** * 在这里  把数据 格式化成  我们需要的 dom节点 列表 */this.createDomHandler(_d[i],type);}/** * 结束之后 调用 showDomsHandler 展示数据 */if(!type) arguments.callee.call(this,this._data,true);},Stree.prototype.showDomsHandler = function(_d){if(!_d) return false;/** * 在浏览器展现数据 */var _groups = _d["group"],_id = _d["id"];if(!_groups){this._pNode.appendChild( this._doms[_id] );}else{this._doms[_groups].appendChild( this._doms[_id] );}},Stree.prototype.createTreeBoxDom = function(_d){if( !_d ) return false;this._doms[_d["id"]] = this.singleDom( {"name": "div","cl": !_d["group"] ? "tree-list-box line-box line-box-f" : ( !_d["info"] ? "tree-list-box" : "tree-list-box line-box" )} );this.createTreeListDom(_d);},Stree.prototype.createTreeListDom = function(_d){var _dom = '';_dom = this.singleDom( {"name": "span","cl": !_d["group"] ? "tree-list tree-list1" : "tree-list"} );_dom.appendChild( this.singleDom( {"name": "span","cl": "tree-select","type": "click","handler": this.treeSelectHandler,"attr": [{"key": "data-id","value": _d["id"]}]} ) );_dom.appendChild( this.singleDom( {"name": "span","cl": "tree-info","_html": _d["name"],"type": "click","handler": this.treeInfoHandler,} ) );this._doms[_d["id"]].appendChild( _dom );},Stree.prototype.treeInfoHandler = function(_this,el){if(el == null){var _pNode = this._pNode,_tree_info = _pNode.querySelector(".tree-info"),_tree_boxs = _pNode.querySelectorAll(".tree-list-box"),_height = _tree_info.offsetHeight + 3;Array.prototype.forEach.call(_tree_boxs,function(item){if( !!item.querySelector(".tree-list-box") ){item.style.height = _height + "px";}else{item.style.height = _height + 2 + "px";}})return false;}var _elps = el.parentNode.parentNode,_height = el.offsetHeight + 3,_pheight = _elps.offsetHeight;!!_elps.querySelector(".tree-list-box") ? (_elps.style.height = _pheight == _height ? "auto" : (_height + "px")) : '';},Stree.prototype.singleDom = function(options){/** * name 必填  否则报错 */var el,attr = options["attr"],type = options["type"],init = options["init"];if(!options || !options["name"]) throw new Error("options is error");el = doc.createElement(options["name"]);el.className = options["cl"] || '';el.innerHTML = options["_html"] || '';if(attr){attr.forEach(function(item,index){el.setAttribute(item["key"], item["value"]);})}if(type){this.addEvent(el,type,options["handler"],false);}return el;},Stree.prototype.addEvent = function(el, eType, handle, bol){//this._bindFunc[eType + el.toString()] = handle.bind(null,this,el);//这里的做法是便于 后面的解绑if(el.addEventListener){           //如果支持addEventListener        el.addEventListener(eType,  handle.bind(null,this,el), bol);    }else if(el.attachEvent){          //如果支持attachEvent        el.attachEvent("on"+eType,  handle.bind(null,this,el));    }else{                                  //否则使用兼容的onclick绑定        el["on"+eType] =  handle.bind(null,this,el);    }},Stree.prototype.removeEvent = function(el, eType, handle, bol){if(el.addEventListener){        el.removeEventListener(eType, handle, bol);    }else if(el.attachEvent){        el.detachEvent("on"+eType, handle);    }else{        el["on"+eType] = null;    }},Stree;})(win,doc),win.Stree = Stree;})(window,document)


html: 

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Stree</title><meta name="viewport" content="target-densitydpi=device-dpi,width=600,user-scalable=no" /><link rel="stylesheet" type="text/css" href="css/stree.css" /></head><body><div class="main"><input type="text" name="" class="s-tree-search" placeholder="请输入关键字按enter搜索" /><div class="s-tree"></div></div><script type="text/javascript" src="js/stree.js"></script><script type="text/javascript">var data = [];//模拟1800条数据for(let i=1;i<11;i++){//一级结构var _info1 = [],_id = "a" + i;data.push({"name": "我是一级目录"+i,"group": '',"id": _id,"info": _info1})for(let i=1;i<7;i++){var _info2 = [],_group = _id,_id2 = _group + i;_info1.push( {"name": "我是二级目录"+i,"group": _group,"id": _id2,"info": _info2} )for(let i=1;i<4;i++){var _info3 = [],_group2 = _id2,_id3 = _group2 + i;_info2.push( {"name": "我是三级目录"+i,"group": _group2,"id": _id3,"info": _info3} )for(let i=1;i<3;i++){var _info4 = [],_group3 = _id3,_id4 = _group3 + i;_info3.push( {"name": "我是四级目录"+i,"group": _group3,"id": _id4,"info": _info4} )for(let i=1;i<6;i++){var _info5 = [],_group4 = _id4,_id5 = _group4 + i;_info4.push( {"name": "我是五级目录"+i,"group": _group4,"id": _id5,"info": ''} )}}}}}console.log(data);new Stree({el: document.querySelector(".s-tree"),search: document.querySelector(".s-tree-search"),data: data});</script></body></html>










原创粉丝点击