You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

三级层级DataTable子行点击无法展开孙行的修复咨询

三级层级表格展开问题修复方案

问题说明

我编写了一套逻辑实现三级层级表格(数据结构为一级→二级→三级),能正常生成表格结构,但点击二级表格(child-table)的行时,无法展开对应的三级表格(grand-child table),求修复方案或替代实现方式。

示例数据

example_data = [
    {
        "Approved": 0,
        "Denied": 1,
        "tat_avg": 0,
        "Name": "Total Care- PREFERRED",
        "Aetna": {
            "Approved": 0,
            "Denied": 1,
            "tat_avg": 0,
            "Name": "Aetna",
            "Humira": {
                "Approved": 0,
                "Denied": 1,
                "tat_avg": 0,
                "Name": "Humira"
            }
        }
    },
    {
        "Approved": 1,
        "Denied": 0,
        "tat_avg": 0,
        "Name": "Acaria Specialty Pharmacy",
        "test bin": {
            "Approved": 1,
            "Denied": 0,
            "tat_avg": 0,
            "Name": "test bin",
            "Aveed": {
                "Approved": 1,
                "Denied": 0,
                "tat_avg": 0,
                "Name": "Aveed"
            }
        }
    },
]

原实现代码

$.ajax({
    url: "{{ path('api_path') }}",
    method: 'GET',
    dataType: 'json',
    data: {enterpriseID:{{user.enterpriseID}}, tab: 'pharmacy_analysis', start: startdate, end: enddate},
    success: function (data) {
        pharmacy_analysis_data = example_data;

        // Destroy the existing DataTable if it already exists
        if ($.fn.DataTable.isDataTable('#example1')) {
        $('#example1').DataTable().destroy();
        }

        // Initialize DataTable with new data
        let table1 = $('#example1').DataTable({
        data: pharmacy_analysis_data,
        scrollY: 500,
        paging: false,
        columns: [
            {
                data: null,
                className: 'dt-control',
                orderable: false,
                defaultContent: '',
            },
            { data: 'Name' },
            { data: 'Approved' },
            { data: 'Denied' },
            { data: 'tat_avg' }
        ],
        order: [[1, 'asc']],
        language: {
            emptyTable: "No data available"
        }
        });
        
        table1.on('click', 'td.dt-control', function (e) {
        let tr = e.target.closest('tr');
        let row = table1.row(tr);

        if (row.child.isShown()) {
            // This row is already open - close it
            row.child.hide();
            tr.classList.remove('shown');
        } else {
            // Open this row
            row.child(formatChild(row.data())).show();
            tr.classList.add('shown');
            var childTable = $(row.node()).next().find('.child-table');

            // Handle click events for child rows
            childTable.on('click', 'td.dt-control', function () {
                var tr = $(this).closest('tr');
                var childRow = childTable.DataTable().row(tr);
                console.log(childRow);
                console.log(childRow.data());
                if (childRow.child.isShown()) {
                    childRow.child.hide();
                    tr.classList.remove('shown');
                } else {
                    childRow.child(formatGrandChild(childRow.data())).show();
                    tr.classList.add('shown');
                }
            });
        }
        });

        function formatChild(data) {
        var childTable = '';

        // Check if the data has nested elements
        for (const key in data) {
            if (data.hasOwnProperty(key) && typeof data[key] === 'object') {
                const subArray = data[key];
                childTable +=
                '<table class="child-table" style="width:100%">' +
                '<tr class="child-row">' +
                '<td class="child-dt-control dt-control"></td>' +
                '<td class="child-dt-control">' + subArray.Name + '</td>' +
                '<td class="child-dt-control">' + subArray.Approved + '</td>' +
                '<td class="child-dt-control">' + subArray.Denied + '</td>' +
                '<td class="child-dt-control">' + subArray.tat_avg + '</td>' +
                '</tr>';

                // If there are further nested elements, call formatGrandChild recursively
                childTable += formatGrandChild(subArray);
            }
        }

        return childTable;
        }

        function formatGrandChild(data) {
        var grandChildTable = '';

        // Check if the data has nested elements
        for (const key in data) {
            if (data.hasOwnProperty(key) && typeof data[key] === 'object') {
                const subArray = data[key];
                grandChildTable +=
                    '<table class="grandchild-table" style="width:100%">' +
                    '<tr class="grandchild-row">' +
                    '<td class="grandchild-dt-control dt-control"></td>' +
                    '<td class="grandchild-dt">' + subArray.Name + '</td>' +
                    '<td class="grandchild-dt">' + subArray.Approved + '</td>' +
                    '<td class="grandchild-dt">' + subArray.Denied + '</td>' +
                    '<td class="grandchild-dt">' + subArray.tat_avg + '</td>' +
                    '</tr>' +
                    '</table>';
            }
        }

        return grandChildTable;
        }
    },
    error: function (err) {
        console.log(err);
    },
});

问题原因及修复方案

核心问题

  1. 二级表格未初始化DataTable实例:原代码仅拼接了二级表格的HTML结构,但没有将其初始化为DataTable对象,导致调用childTable.DataTable().row(tr)无法获取有效行数据,展开逻辑失效。
  2. 三级表格提前渲染:原代码在生成二级表格时直接渲染三级表格,不符合点击展开的交互逻辑。

修复后的完整代码

$.ajax({
    url: "{{ path('api_path') }}",
    method: 'GET',
    dataType: 'json',
    data: {enterpriseID:{{user.enterpriseID}}, tab: 'pharmacy_analysis', start: startdate, end: enddate},
    success: function (data) {
        pharmacy_analysis_data = example_data;

        // 销毁已存在的DataTable实例
        if ($.fn.DataTable.isDataTable('#example1')) {
            $('#example1').DataTable().destroy();
        }

        // 初始化一级表格
        const parentTable = $('#example1').DataTable({
            data: pharmacy_analysis_data,
            scrollY: 500,
            paging: false,
            columns: [
                {
                    data: null,
                    className: 'dt-control',
                    orderable: false,
                    defaultContent: '',
                },
                { data: 'Name' },
                { data: 'Approved' },
                { data: 'Denied' },
                { data: 'tat_avg' }
            ],
            order: [[1, 'asc']],
            language: {
                emptyTable: "No data available"
            }
        });

        // 一级表格行点击事件
        parentTable.on('click', 'td.dt-control', function (e) {
            const tr = e.target.closest('tr');
            const row = parentTable.row(tr);

            if (row.child.isShown()) {
                row.child.hide();
                tr.classList.remove('shown');
            } else {
                // 创建二级表格容器
                row.child('<table class="child-table" style="width:100%"></table>').show();
                tr.classList.add('shown');

                // 提取二级表格数据
                const childData = [];
                for (const key in row.data()) {
                    if (typeof row.data()[key] === 'object' && row.data()[key].Name) {
                        childData.push(row.data()[key]);
                    }
                }

                // 初始化二级表格
                const childTable = $(row.node()).next().find('.child-table').DataTable({
                    data: childData,
                    paging: false,
                    ordering: false,
                    columns: [
                        {
                            data: null,
                            className: 'dt-control',
                            orderable: false,
                            defaultContent: '',
                        },
                        { data: 'Name' },
                        { data: 'Approved' },
                        { data: 'Denied' },
                        { data: 'tat_avg' }
                    ],
                    language: {
                        emptyTable: "No data available"
                    }
                });

                // 二级表格行点击事件
                childTable.on('click', 'td.dt-control', function (e) {
                    const tr = e.target.closest('tr');
                    const childRow = childTable.row(tr);

                    if (childRow.child.isShown()) {
                        childRow.child.hide();
                        tr.classList.remove('shown');
                    } else {
                        // 创建三级表格容器
                        childRow.child('<table class="grandchild-table" style="width:100%"></table>').show();
                        tr.classList.add('shown');

                        // 提取三级表格数据
                        const grandChildData = [];
                        for (const key in childRow.data()) {
                            if (typeof childRow.data()[key] === 'object' && childRow.data()[key].Name) {
                                grandChildData.push(childRow.data()[key]);
                            }
                        }

                        // 初始化三级表格
                        $(childRow.node()).next().find('.grandchild-table').DataTable({
                            data: grandChildData,
                            paging: false,
                            ordering: false,
                            columns: [
                                { data: 'Name' },
                                { data: 'Approved' },
                                { data: 'Denied' },
                                { data: 'tat_avg' }
                            ],
                            language: {
                                emptyTable: "No data available"
                            }
                        });
                    }
                });
            }
        });
    },
    error: function (err) {
        console.log(err);
    },
});

修复要点说明

  • 分层初始化DataTable:一级、二级表格都单独初始化DataTable实例,确保行数据能被正确识别和操作。
  • 按需渲染子表格:点击父行时才初始化对应子表格,点击子行时才初始化三级表格,避免提前加载冗余内容。
  • 规范数据提取:遍历父数据时,仅收集包含Name属性的对象作为子表格数据源,过滤无效数据。
  • 实例化绑定事件:每个表格实例单独绑定点击事件,避免事件冒泡或目标混淆。

内容的提问来源于stack exchange,提问作者Akshay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 19:20:57