基于jQuery的Bootstrap树形组件配置与级联交互实现
运行环境依赖
在部分轻量级项目或无需构建工具的传统架构中,基于 jQuery 的 UI 插件依然是高效实现复杂交互的首选。Bootstrap Treeview 凭借其良好的兼容性与直观的层级渲染效果,常被用于组织架构、权限分配或目录导航等场景。该组件强依赖于 Bootstrap 3.x 体系与 jQuery 1.9+ 版本,引入时需注意版本兼容性。
<!-- 样式文件 -->
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bootstrap-treeview.min.css">
<!-- 脚本文件 -->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<script src="assets/js/bootstrap-treeview.min.js"></script>
<!-- 容器占位 -->
<div id="dataTree"></div>
数据结构规范与初始化
组件要求传入符合特定规范的 JSON 数组。每个节点对象可定义显示文本、图标、颜色、状态及子节点集合。以下示例演示了如何构建基础层级数据并完成组件挂载:
const menuSource = [
{
text: '系统管理',
icon: 'glyphicon glyphicon-cog',
color: '#333',
selectable: false,
state: { expanded: true },
nodes: [
{ text: '用户权限', state: { checked: false } },
{ text: '日志审计', href: '#/logs' }
]
},
{
text: '业务中心',
nodes: [
{ text: '订单处理' },
{ text: '库存监控', tags: ['核心', '高频'] }
]
}
];
$(function() {
$('#dataTree').treeview({
data: menuSource,
levels: 2,
showCheckbox: true,
showIcon: true,
onhoverColor: '#e8f0fe',
highlightSearchResults: true
});
});
关键配置参数
初始化时可传入一个配置对象以定制外观与行为。常用参数包括:
data:必填,节点数据源。levels:限制最大渲染层级。showCheckbox/showIcon:控制复选框与节点图标的显隐。nodeIcon/checkedIcon/expandIcon:自定义默认、选中及展开状态的图标类名。color/backColor:全局文本与背景颜色,可被单节点配置覆盖。enableLinks:启用后将节点文本渲染为超链接,需配合数据中的href属性。multiSelect:允许同时选中多个独立节点。
API 调用与事件机制
组件提供了完整的 API 供运行时控制。调用方式分为 jQuery 包装器模式与实例模式。例如展开所有节点可写为 $('#dataTree').treeview('expandAll') 或 $('#dataTree').data('treeview').expandAll()。核心方法涵盖状态切换(checkNode, disableNode)、树形操作(collapseAll, search)以及数据获取(getNode, getSelected)。
交互反馈通过事件监听实现。支持两种绑定方式:一是在初始化配置中使用 onXxx 回调,二是通过标准的 jQuery 事件机制监听:
$('#dataTree').on('nodeSelected', function(event, nodeData) {
console.log('当前选中节点:', nodeData.text);
});
常用事件包括 nodeChecked、nodeUnchecked、nodeCollapsed、nodeExpanded 以及 searchComplete。
父子级联选择逻辑实现
官方提供的 hierarchicalCheck 参数在实际项目中常出现状态不同步或性能问题。推荐采用手动拦截事件并结合递归算法的方式实现可靠的级联控制。以下方案通过统一监听选中与取消事件,动态收集受影响的路径节点,并使用 silent: true 参数避免触发死循环:
// 向上递归收集父级节点ID
function collectParentIds(currentNode, idCollection) {
const parentRef = $('#dataTree').treeview('getParent', currentNode.nodeId);
if (parentRef && parentRef.nodeId) {
idCollection.push(parentRef.nodeId);
collectParentIds(parentRef, idCollection);
}
}
// 向下递归收集子级节点ID
function collectDescendantIds(nodeObj, idCollection) {
idCollection.push(nodeObj.nodeId);
if (nodeObj.nodes && nodeObj.nodes.length > 0) {
nodeObj.nodes.forEach(child => collectDescendantIds(child, idCollection));
}
}
// 统一处理级联逻辑
$('#dataTree').on('nodeChecked nodeUnchecked', function(event, activeNode) {
const isCheck = event.type === 'nodeChecked';
const targetIds = [];
if (isCheck) {
// 选中时:联动所有父节点与所有子节点
collectParentIds(activeNode, targetIds);
collectDescendantIds(activeNode, targetIds);
} else {
// 取消选中时:仅当所有兄弟节点均未选中时,才取消父节点
const siblingNodes = $('#dataTree').treeview('getSiblings', activeNode.nodeId);
const allSiblingsUnchecked = siblingNodes.every(s => !s.state.checked);
if (allSiblingsUnchecked) {
collectParentIds(activeNode, targetIds);
}
// 始终取消所有子节点
collectDescendantIds(activeNode, targetIds);
}
// 批量执行状态变更,silent参数防止递归触发自身事件
if (targetIds.length > 0) {
const method = isCheck ? 'checkNode' : 'uncheckNode';
$('#dataTree').treeview(method, [targetIds, { silent: true }]);
}
});