0、写在前面的话
最近看书都懈怠了,又正值新项目,虽说并不是忙得不可开交,好吧我老实交待,我就是偷懒了其实,博客也没更。言归正传,对于前端的不熟悉现在确实是个让我头疼的事情,以至于一些功能要在网络上漫天飞舞疯狂尝试才能做得出来,关键是特别费时间,确实得抽时间补补前端的基础才是。这次要实现一个分类选择,即图片上传之前,抓取所有的图片分类,然后在上传页面做成下拉菜单栏进行选择。效果如下图,理论上要达到无限层级的下拉菜单:

1、bootstrap的按钮下拉菜单栏
bootstrap是有直接可以使用的下拉菜单插件的:

如上图,这个效果差不多就是我们想要的样式了,可是该插件是不支持无限层级分支的,也就是说只有如上图方式的主菜单“Java”和其子项。
2、无限层级的bootstrap按钮下拉菜单栏
然而网友的力量是强大的,在bootstrap按钮下拉菜单栏的基础上,网友拓展实现了无限层级子菜单,参见:

可以看到这个效果绝对是非常棒了,that's what I want!
说动手就动手,通过作者给的demo可以看到:
<li class="offset-left dropdown">
<a href="#" class="dropdown-toggle">Dropdown...</a>
<ul class="dropdown-menu">
<li><a href="#">Secondary link</a></li>
<li><a href="#">Something else here</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Another link</a></li>
</ul>
</li>10
1
<li class="offset-left dropdown"> 2
<a href="#" class="dropdown-toggle">Dropdown...</a>3
<ul class="dropdown-menu">4
<li><a href="#">Secondary link</a></li>5
<li><a href="#">Something else here</a></li>6
<li><a href="#">Something else here</a></li>7
<li class="divider"></li>8
<li><a href="#">Another link</a></li>9
</ul>10
</li>凡是有子菜单延伸的条目,由普通的<li><a></a></li>变成如上形式即可。
3、前后端交互,前端数据的展示
3.1 struts标签的失败和启示
前端页面使用的JSP,项目使用了Struts,所以前端是可以直接使用struts的标签的。也就是说,列表实体类可以直接通过struts抓取其中的属性List,再通过<s:iterator>,如<s:iterator value="category.childCategoryList">,然后通过if else判定,如果没有子类,则设定其样式形为<li><a></a></li>,如果有子类,则设定其样式形为<li class="offset-left dropdown"><a href="#" class="dropdown-toggle">MenuName</a><ul class="dropdown-menu"><li><a></a></li></ul></li>
然而问题在于,每当子节点存在下一层子节点,我又必须增加一层if else判定,假如规定子分类最多4层,那么我的if判定也就写4层好了,可是我们这个是理论上无限层级,假如用户就是不断建立分类细化,那怎么办?显然,这种方式是不合理的,而struts标签页没有其他更好的方式来解决这种问题。
这种失败在于数据的局限性,数据应该是后台处理好之后传给前端,仅由前端来展示即可,而如上struts是将数据一股脑丢到前台,由前端来筛选组合处理,再进行展示,麻烦不言而喻。所以实际上应该将数据处理好,固化后再给前端解析使用。一旦数据结构固化,前端通过分析该数据,也就知道怎么做了。现在一般是使用Json,所以在我自己的这个例子中,我将后端的分类和子分类之间的关系,在后端转换成json,传递给了前端,json结构如下:
[
{
"children": [
{
"children": [
{
"children": [],
"id": 10,
"text": "公司"
},
{
"children": [],
"id": 11,
"text": "兼职"
}
],
"id": 3,
"text": "工作"
},
{
"children": [],
"id": 4,
"text": "生活"
},
{
"children": [],
"id": 5,
"text": "个人"
}
],
"id": 2,
"text": "日常"
},
{
"children": [],
"id": 6,
"text": "高清大图"
},
{
"children": [
{
"children": [],
"id": 8,
"text": "修改"
},
{
"children": [],
"id": 9,
"text": "测试回调"
}
],
"id": 7,
"text": "测试"
}
]55
1
[2
{3
"children": [4
{5
"children": [6
{7
"children": [],8
"id": 10,9
"text": "公司"10
},11
{12
"children": [],13
"id": 11,14
"text": "兼职"15
}16
],17
"id": 3,18
"text": "工作"19
},20
{21
"children": [],22
"id": 4,23
"text": "生活"24
},25
{26
"children": [],27
"id": 5,28
"text": "个人"29
}30
],31
"id": 2,32
"text": "日常"33
},34
{35
"children": [],36
"id": 6,37
"text": "高清大图"38
},39
{40
"children": [41
{42
"children": [],43
"id": 8,44
"text": "修改"45
},46
{47
"children": [],48
"id": 9,49
"text": "测试回调"50
}51
],52
"id": 7,53
"text": "测试"54
}55
]3.2 递归生成html
有了数据,只需要通过数据解析,然后组成相关的html代码即可。如上的json结构,可以看到只要有chidren属性不为空,那么我们就可以在该部分生成子菜单形式的样式,否则使用普通的样式。这里就要用到递归了,因为是无限层级,否则使用if判定则又和struts一样进入了死胡同。
菜单部分的html是通过数据和算法生成的,不是固定的,需要提醒一下。则正常时空空如也:
<td>分类<b>*</b><br/>
<div class="btn-group">
<button id="categoryButton" idx="0" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" style="215px">
请选择图片分类<span class="caret"></span>
</button>
<!--图片分类的下拉菜单列表,通过js加载数据-->
<ul id="categories" class="dropdown-menu" role="menu">
<!--loading data in here-->
</ul>
</div>
</td>11
1
<td>分类<b>*</b><br/>2
<div class="btn-group">3
<button id="categoryButton" idx="0" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" style="215px">4
请选择图片分类<span class="caret"></span>5
</button>6
<!--图片分类的下拉菜单列表,通过js加载数据-->7
<ul id="categories" class="dropdown-menu" role="menu">8
<!--loading data in here-->9
</ul>10
</div>11
</td>后端给前端传递的json数据,变量名这里为categories,即如下的var data,那么接下来通过一个递归方法:
var glb_str = "";
function createCategoryTree(data) {
var flag;
for (var i = 0; i < data.length; i++) {
var nodeText = data[i]['text'];
var nodeId = data[i]['id'];
var children = data[i]['children'];
//若有子分类,则遍历子分类
if(children.length > 0) {
flag = true;
glb_str += "<li class='offset-right dropdown'><a class='category' " + "id=" + nodeId + " href='#'>" + nodeText + "...</a><ul class='dropdown-menu'>"
createCategoryTree(children);
} else {
//若没有子分类
flag = false;
glb_str += "<li><a class='category' " + "id=" + nodeId + " href='#'>" + nodeText + "</a>";
}
if (flag) {
glb_str += "</ul></li>"
} else {
glb_str += "</li>";
}
}
return glb_str;
}
//以上为方法定义,该处为调用
var data = ${categories};
var result = createCategoryTree(data);
$("#categories").append(result);x
1
var glb_str = "";2
function createCategoryTree(data) {3
var flag;4
for (var i = 0; i < data.length; i++) {5
var nodeText = data[i]['text'];6
var nodeId = data[i]['id'];7
var children = data[i]['children'];8
//若有子分类,则遍历子分类9
if(children.length > 0) {10
flag = true;11
glb_str += "<li class='offset-right dropdown'><a class='category' " + "id=" + nodeId + " href='#'>" + nodeText + "...</a><ul class='dropdown-menu'>"12
createCategoryTree(children);13
} else {14
//若没有子分类15
flag = false;16
glb_str += "<li><a class='category' " + "id=" + nodeId + " href='#'>" + nodeText + "</a>";17
}18
19
if (flag) {20
glb_str += "</ul></li>"21
} else {22
glb_str += "</li>";23
}24
}25
return glb_str;26
}27
28
//以上为方法定义,该处为调用29
var data = ${categories};30
var result = createCategoryTree(data);31
$("#categories").append(result);加载完成的页面,可以看到,页面源码中已经有列表的html代码了:

4、参考链接
赶时间,写得潦草了些,就这样吧~