看到一款樹形結構,比較喜歡它的樣式,就參照它的外觀本身作了一個,練習一下CSS。css
作出來的效果以下:jquery
樹的dom結構:app
<div class="tree"> <ul> <li> <span><i class="fa fa-minus-circle"></i>拉莫小學</span> <ul> <li> <span><i class="fa fa-minus-circle"></i>一年級</span> <ul> <li><span>一班</span></li><li><span>二班</span></li> </ul> </li> <li> <span>二年級</span> </li> <li> <span><i class="fa fa-minus-circle"></i>三年級</span> <ul> <li><span>一班</span></li> <li><span>二班</span></li> <li><span>三班</span></li> </ul> </li> </ul> </li> </ul> </div>
CSS代碼:dom
/** tree.css zyj 2018.4.21 */ ul,li{list-style-type:none;} .tree{display:block;position:relative;padding:5px 15px;} .tree span{display:inline-block;box-sizing:border-box;height:30px;line-height:28px;min-width:60px;text-align:center;color:#888;border:1px solid #ddd;border-radius:5px;padding:0 8px;} .tree ul{position:relative;padding-left:60px;margin:0;} .tree ul>li{position:relative;padding:5px 0;} .tree>ul{padding:0;margin:0;} /** 水平方向連線 */ .tree>ul ul>li:after{content:' ';position:absolute;top:20px;left:-45px;width:45px;border:none;border-top:1px solid #ddd;} /** 垂直方向連線 */ .tree ul>li:not(:last-child):before{content:' ';position:absolute;top:0;left:-45px;height:100%;border:none;border-left:1px solid #ddd;} .tree ul>li:last-child:before{content:' ';position:absolute;top:0;left:-45px;height:20px;border:none;border-left:1px solid #ddd;} /** 控制鼠標移上去的顏色 */ .tree span:hover, .tree span:hover+ul span{color:#fff;background-color:orange;} .tree span:hover, .tree span:hover+ul span, .tree span:hover+ul li:before, .tree span:hover+ul li:after{border-color:orange;} /** 摺疊圖標 */ .tree .fa:before{margin-right:5px;} .tree .fa-minus-circle, .tree .fa-plus-circle{cursor:pointer;}
裏面引的fontawesome圖標無法加載進來,致使摺疊按鈕顯示不出,下面是原始樹狀圖的截圖:ide
數據是我用JS加載的,寫了個加載數據的tree.js文件,源碼以下:url
/** tree.js zyj 2018.4.22 */ (function(name){ var tree, outer, defaultDateFormat; outer = { setData : setData, }; defaultDateFormat = { unfold : true, name : 'name', childName : 'children' }; function getDataFormat(dataFormat){ var index; if(!dataFormat){ return defaultDateFormat; } for(index in defaultDateFormat){ dataFormat[index] = typeof dataFormat[index] == 'undefined'? defaultDateFormat[index] : dataFormat[index]; } return dataFormat } function initTreeJs(name){ var tree; if(checkTreeNameUsed(name)){return;} window[name] = outer; initFoldIcon($('.tree')); } function checkTreeNameUsed(name){ if(window[name]){ console.error("The window object name [" + name + "] has been used, tree.js can't be loaded! You can try another name." ); return true; } return false; } function initFoldIcon(target){ target.off('click', 'span>i.fa').on('click', 'span>i.fa', function(e){ var ele = $(e.target); if(ele.hasClass('fa-minus-circle')){ ele.removeClass('fa-minus-circle').addClass('fa-plus-circle').parent().next('ul').hide(200); }else if(ele.hasClass('fa-plus-circle')){ ele.removeClass('fa-plus-circle').addClass('fa-minus-circle').parent().next('ul').show(200); } }) } function getJqueryObjectBySelector(selector){ var ele = $(selector); if(typeof selector != 'string'){ console.error("The first parameter jquery selector [" + selector + "] must be a string!" ); return; } if(!ele.hasClass('tree')){ ele = ele.find('.tree'); } if(ele.length != 1){ console.error("The selector [" + selector + "] expect only one element!" ); return; } return ele; } function setData(selector, data, dataFormat){ var ele = getJqueryObjectBySelector(selector); if(!ele){return;} if(!data){return;} if(!data.length){ data = [data]; } dataFormat = getDataFormat(dataFormat); dataFormat.topElement = true; ele.empty().append(getTreeList(data, dataFormat)); initFoldIcon(ele); } function getTreeList(data, dataFormat){ var i, single, name, children, childDataFormat, array = []; childDataFormat = dataFormat.child || dataFormat; if(dataFormat.unfold){ array.push('<ul>'); }else if(dataFormat.topElement){ dataFormat.topElement = false; array.push('<ul>'); }else{ array.push('<ul style="display:none;">'); } for(i=0; i<data.length; i++){ single = data[i]; if(typeof dataFormat.name == 'function'){ name = dataFormat.name(single); }else if(typeof dataFormat.name == 'string'){ name = single[dataFormat.name]; }else{ name = single['name']; } if(typeof dataFormat.childName == 'string'){ children = single[dataFormat.childName]; }else{ children = single['children']; } array.push('<li>'); array.push('<span>'); if(children && children.length > 0){ if(dataFormat.unfold){ array.push('<i class="fa fa-minus-circle"></i>'); }else{ array.push('<i class="fa fa-plus-circle"></i>'); } array.push(name); array.push('</span>'); array.push(getTreeList(children, childDataFormat)); }else{ array.push(name); array.push('</span>'); } array.push('</li>'); } array.push('</ul>'); return array.join(''); } initTreeJs(name); }('tree'))
偷懶沒寫註釋,tree.js中目前只寫了一個對外的接口 tree.setData(selector, data, dataFormat) 。參數selector是jQuery選擇器,data是數據,dataFormat是數據格式。spa
好比加載上圖的數據:code
var dataTest = { name:'拉莫小學', children:[ { name:'一年級', children:[ {name:'一班'}, {name:'二班'} ] }, { name:'二年級' }, { name:'三年級', children:[ {name:'一班'}, {name:'二班'}, {name:'三班'} ] } ] }; tree.setData('.tree', dataTest);
因爲後臺加載的數據不必定是按照{name:'*', children:[{name:'*'},...]}這種結構,因此留了dataFormat參數,本身去定義數據格式。orm
簡單舉個例子,假如後臺數據格式是blog
var data =
{ id : '1', title : '百度', url : 'http://www.baidu.com', subWeb : [ { id : '2', title : '百度新聞', url : 'http://news.baidu.com' }, { id : '3', title : '百度知道', url : 'http://zhidao.baidu.com' }, { id : '4', title : '百度圖片', url : 'http://image.baidu.com' }, ] }
那麼dataFormat能夠定義爲
var dataFormat = { name : function(data){ return '<a href="' + data.url + '">' + data.title + '</a>'; }, childName : 'subWeb' }
至於效果,讀者本身去試咯。