导读:本期聚焦于小伙伴创作的《JavaScript实现动态展开N阶表格与行的完整教程》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《JavaScript实现动态展开N阶表格与行的完整教程》有用,将其分享出去将是对创作者最好的鼓励。

使用JavaScript实现动态展开N阶Table和Row功能

在Web开发中,我们经常需要实现动态展开和折叠的表格功能,类似于FineReport中的效果。本文将介绍如何使用JavaScript实现一个可以动态展开N阶的表格和行功能。

实现思路

实现动态展开N阶表格的核心思路是:

  • 使用递归方式构建表格结构
  • 通过数据驱动视图更新
  • 利用事件委托处理展开/折叠操作
  • 动态计算并应用缩进样式

数据结构设计

首先我们需要设计一个合适的数据结构来存储表格的层级关系:

// 示例数据结构
const tableData = [
    {
        id: 1,
        name: '一级节点1',
        children: [
            {
                id: 11,
                name: '二级节点1-1',
                children: [
                    { id: 111, name: '三级节点1-1-1' },
                    { id: 112, name: '三级节点1-1-2' }
                ]
            },
            { id: 12, name: '二级节点1-2' }
        ]
    },
    {
        id: 2,
        name: '一级节点2'
    }
];

核心实现代码

下面是完整的实现代码,包含HTML结构和JavaScript逻辑:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>动态展开表格</title>
    <style>
        .tree-table {
            width: 100%;
            border-collapse: collapse;
            font-family: Arial, sans-serif;
        }
        .tree-table th, .tree-table td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
        }
        .tree-table th {
            background-color: #f2f2f2;
        }
        .expand-icon {
            cursor: pointer;
            margin-right: 5px;
            display: inline-block;
            width: 16px;
            text-align: center;
        }
        .indent {
            display: inline-block;
        }
        .hidden {
            display: none;
        }
    </style>
</head>
<body>
    <table class="tree-table" id="dynamicTable">
        <thead>
            <tr>
                <th>名称</th>
                <th>层级</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody id="tableBody">
            <!-- 表格内容将通过JavaScript动态生成 -->
        </tbody>
    </table>

    <script>
        // 示例数据
        const tableData = [
            {
                id: 1,
                name: '一级节点1',
                children: [
                    {
                        id: 11,
                        name: '二级节点1-1',
                        children: [
                            { id: 111, name: '三级节点1-1-1' },
                            { id: 112, name: '三级节点1-1-2' }
                        ]
                    },
                    { id: 12, name: '二级节点1-2' }
                ]
            },
            {
                id: 2,
                name: '一级节点2'
            }
        ];

        // 存储展开状态
        const expandedState = new Set();

        // 渲染表格
        function renderTable(data, parentId = null, level = 0) {
            const tbody = document.getElementById('tableBody');
            let html = '';
            
            data.forEach(item => {
                const hasChildren = item.children && item.children.length > 0;
                const isExpanded = expandedState.has(item.id);
                const indentStyle = `padding-left: ${level * 20}px;`;
                
                html += `
                    <tr data-id="${item.id}" data-level="${level}" data-parent-id="${parentId}">
                        <td>
                            <span class="indent" style="${indentStyle}"></span>
                            ${hasChildren ? 
                                `<span class="expand-icon" onclick="toggleExpand(${item.id})">${isExpanded ? '-' : '+'}</span>` : 
                                '<span class="expand-icon">&nbsp;</span>'}
                            ${item.name}
                        </td>
                        <td>${level + 1}</td>
                        <td>
                            <button onclick="addChild(${item.id})">添加子节点</button>
                            <button onclick="deleteNode(${item.id})">删除</button>
                        </td>
                    </tr>
                `;
                
                // 如果有子节点且处于展开状态,递归渲染子节点
                if (hasChildren && isExpanded) {
                    html += renderTable(item.children, item.id, level + 1);
                }
            });
            
            return html;
        }

        // 切换展开/折叠状态
        function toggleExpand(id) {
            if (expandedState.has(id)) {
                expandedState.delete(id);
            } else {
                expandedState.add(id);
            }
            refreshTable();
        }

        // 刷新表格
        function refreshTable() {
            const tbody = document.getElementById('tableBody');
            tbody.innerHTML = renderTable(tableData);
        }

        // 添加子节点
        function addChild(parentId) {
            const parentNode = findNodeById(tableData, parentId);
            if (!parentNode.children) {
                parentNode.children = [];
            }
            
            const newNode = {
                id: Date.now(), // 使用时间戳作为临时ID
                name: `新节点-${parentNode.children.length + 1}`,
                children: []
            };
            
            parentNode.children.push(newNode);
            expandedState.add(parentId); // 确保父节点展开
            refreshTable();
        }

        // 删除节点
        function deleteNode(id) {
            if (confirm('确定要删除这个节点及其所有子节点吗?')) {
                deleteNodeById(tableData, id);
                refreshTable();
            }
        }

        // 根据ID查找节点
        function findNodeById(data, id) {
            for (const item of data) {
                if (item.id === id) {
                    return item;
                }
                if (item.children) {
                    const found = findNodeById(item.children, id);
                    if (found) {
                        return found;
                    }
                }
            }
            return null;
        }

        // 根据ID删除节点
        function deleteNodeById(data, id) {
            for (let i = 0; i < data.length; i++) {
                if (data[i].id === id) {
                    data.splice(i, 1);
                    return true;
                }
                if (data[i].children) {
                    if (deleteNodeById(data[i].children, id)) {
                        return true;
                    }
                }
            }
            return false;
        }

        // 初始化表格
        document.addEventListener('DOMContentLoaded', () => {
            refreshTable();
        });
    </script>
</body>
</html>

代码解析

1. 数据结构

我们使用嵌套的对象数组来表示树形结构,每个节点包含id、name和可选的children属性。

2. 渲染逻辑

renderTable函数是核心,它递归地遍历数据并生成HTML:

  • 根据层级计算缩进样式
  • 为有子节点的行添加展开/折叠图标
  • 根据展开状态决定是否渲染子节点

3. 状态管理

使用Set对象expandedState来存储当前展开的节点ID,这样可以高效地判断和更新展开状态。

4. 交互功能

实现了以下交互功能:

  • 点击+/-图标展开/折叠子节点
  • 添加子节点
  • 删除节点及其所有子节点

扩展功能

基于上述基础实现,我们可以进一步扩展以下功能:

1. 懒加载

对于大数据量的场景,可以实现懒加载:

// 懒加载示例
async function loadChildren(parentId) {
    // 模拟API调用
    const response = await fetch(`/api/children/${parentId}`);
    const children = await response.json();
    
    const parentNode = findNodeById(tableData, parentId);
    parentNode.children = children;
    
    expandedState.add(parentId);
    refreshTable();
}

2. 拖拽排序

可以添加拖拽排序功能,让用户能够重新排列节点顺序。

3. 编辑功能

实现双击单元格编辑节点名称的功能。

性能优化建议

对于大型数据集,可以考虑以下优化措施:

  • 使用虚拟滚动技术只渲染可见区域的内容
  • 对频繁操作的DOM元素进行缓存
  • 使用文档片段减少重排次数
  • 考虑使用Web Worker处理复杂的数据操作

总结

本文介绍了如何使用JavaScript实现一个动态展开N阶的表格功能。通过递归渲染、状态管理和事件处理,我们可以创建出灵活且功能丰富的树形表格。该实现可以进一步扩展,以满足更复杂的需求,如懒加载、拖拽排序和实时编辑等。

这种实现方式的优点在于代码结构清晰,易于理解和维护,同时具有良好的扩展性,可以根据具体需求进行定制和增强。

JavaScript动态表格N阶展开树形表格前端开发

免责声明:已尽一切努力确保本网站所含信息的准确性。网站部分内容来源于网络或由用户自行发表,内容观点不代表本站立场。本站是个人网站免费分享,内容仅供个人学习、研究或参考使用,如内容中引用了第三方作品,其版权归原作者所有。若内容触犯了您的权益,请联系我们进行处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。前端、网络、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握网站开发与运维所需的核心技术栈。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端逻辑,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。