如何实现支持无限层级嵌套的DataTables?
DataTables无限层级嵌套行实现方案
需求说明
需要实现支持无限层级嵌套的DataTables:点击行前的展开按钮可显示子表格,子表格的行同样支持展开/收起操作,以此类推实现无限层级的嵌套结构。
核心实现思路
- 递归数据结构:每个数据节点包含
details字段,用于存储当前行的子级数据,天然支持无限嵌套 - 通用表格初始化函数:封装可复用的表格创建逻辑,递归处理子表格的初始化流程
- 事件委托处理:利用事件委托绑定展开/收起事件,避免为每个动态创建的子表格重复绑定事件
- 唯一ID管理:维护全局计数器,确保每个嵌套表格的ID唯一,避免DOM元素冲突
完整代码示例
HTML 结构
<!DOCTYPE html> <html> <head> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.25/css/jquery.dataTables.min.css"> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js"></script> <style> .details td { border-left: 2px solid #ddd; padding-left: 20px; } .control img { cursor: pointer; } </style> </head> <body> <table id="mainTable" class="display" style="width:100%"> <thead> <tr> <th></th> <th>Race</th> <th>Year</th> <th>Total</th> </tr> </thead> </table> </body> </html>
JavaScript 代码
// 全局计数器,确保每个子表格ID唯一 let tableCounter = 1; // 生成嵌套表格的HTML结构 function generateSubTableHtml(tableId) { return `<table id="subTable_${tableId}" class="display" style="width:100%"></table>`; } // 通用表格初始化函数,支持递归创建嵌套表格 function initTable(tableSelector, data, columns, isMainTable = false) { const table = $(tableSelector).DataTable({ data: data, paging: false, ordering: true, info: true, columns: columns, columnDefs: isMainTable ? [] : [ // 子表格的图片列渲染 { targets: 1, render: function(data) { return `<img width="80px" src="${data}" alt="Player">`; } } ], rowCallback: function(row, rowData) { // 仅当当前行有子数据时,添加展开按钮 if (rowData.details && rowData.details.length > 0) { $(row).find('td:first-child').html('<img src="https://i.imgur.com/SD7Dz.png" alt="Expand">'); } else { $(row).find('td:first-child').html(''); } } }); // 事件委托绑定展开/收起事件,支持动态创建的表格 $(tableSelector).on('click', 'td.control img', function() { const tr = $(this).closest('tr'); const row = table.row(tr); const rowData = row.data(); if (row.child.isShown()) { // 收起子表格 this.src = 'https://i.imgur.com/SD7Dz.png'; row.child.hide(); tr.removeClass('shown'); } else { // 展开子表格 this.src = 'https://i.imgur.com/d4ICC.png'; const subTableId = tableCounter++; const subTableHtml = generateSubTableHtml(subTableId); row.child(subTableHtml, 'details').show(); tr.addClass('shown'); // 递归初始化子表格 initTable(`#subTable_${subTableId}`, rowData.details, [ { data: null, className: 'control', orderable: false }, { data: 'pic' }, { data: 'name' }, { data: 'team' }, { data: 'server' } ]); } }); return table; } // 递归结构的测试数据(可无限添加嵌套层级) const terranImage = "https://i.imgur.com/HhCfFSb.jpg"; const jaedongImage = "https://i.imgur.com/s3OMQ09.png"; const grubbyImage = "https://i.imgur.com/wnEiUxt.png"; const stephanoImage = "https://i.imgur.com/vYJHVSQ.jpg"; const scarlettImage = "https://i.imgur.com/zKamh3P.jpg"; // 三级嵌套数据示例 const level3Data = [{ pic: terranImage, name: "Bomber", team: "independent", server: "NA", details: [] // 可继续添加子数据实现更深层级 }]; const level2Data = [ { pic: jaedongImage, name: "Jaedong", team: "evil geniuses", server: "NA", details: level3Data }, { pic: scarlettImage, name: "Scarlett", team: "acer", server: "Europe", details: [] }, { pic: stephanoImage, name: "Stephano", team: "evil geniuses", server: "Europe", details: [] } ]; const mainData = [ { race: "Zerg", year: "2014", total: "3", details: level2Data }, { race: "Protoss", year: "2014", total: "1", details: [{ pic: grubbyImage, name: "Grubby", team: "independent", server: "Europe", details: [] }] }, { race: "Terran", year: "2014", total: "1", details: level3Data } ]; // 初始化主表格 $(document).ready(function() { initTable('#mainTable', mainData, [ { data: null, className: 'control', orderable: false }, { data: 'race' }, { data: 'year' }, { data: 'total' } ], true); });
代码关键说明
- 递归数据结构:每个数据对象都包含
details数组,用于存储子级数据,可无限扩展嵌套层级 - 通用初始化函数:
initTable函数统一处理表格初始化、行回调、事件绑定,递归创建子表格,避免重复代码 - 事件委托:通过
$(tableSelector).on('click', 'td.control img', ...)绑定事件,自动适配动态创建的子表格 - 唯一ID管理:全局计数器
tableCounter确保每个子表格ID唯一,避免DOM元素ID冲突 - 动态按钮控制:在
rowCallback中根据当前行是否有子数据,动态显示/隐藏展开按钮
内容的提问来源于stack exchange,提问作者showtime
相关产品推荐
相关产品推荐

