看到一款树形结构,比较喜欢它的样式,就参照它的外观自己做了一个,练习一下CSS。
做出来的效果如下:
- 拉莫小学
- 一年级
- 一班
- 二班
- 二年级
- 三年级
- 一班
- 二班
- 三班
- 一年级
树的dom结构:
<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代码:
/** 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图标没法加载进来,导致折叠按钮显示不出,下面是原始树状图的截图:
数据是我用JS加载的,写了个加载数据的tree.js文件,源码如下:
/** 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是数据格式。
比如加载上图的数据:
var dataTest = {
name:'拉莫小学',
children:[
{
name:'一年级',
children:[
{name:'一班'},
{name:'二班'}
]
},
{
name:'二年级'
},
{
name:'三年级',
children:[
{name:'一班'},
{name:'二班'},
{name:'三班'}
]
}
]
};
tree.setData('.tree', dataTest);
由于后台加载的数据不一定是按照{name:'*', children:[{name:'*'},...]}这种结构,所以留了dataFormat参数,自己去定义数据格式。
简单举个例子,假如后台数据格式是
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'
}
至于效果,读者自己去试咯。