原生JS实现树级表格

在这里插入图片描述

思想:一级数据直接展示,其余的数据暂时不生成。带到点击某一个一级在去生成其对应的二级数据,并且后续只需要判断是否存在,存在则隐藏。需要准备一个记录隐藏状态的数组,及存子集的对象,便于快速访问。在tr上可以通过处理好的数据添加自定义属性,便于后续操作。实列中的图标可以自己去引入,使用UI组件库中的图标,对于none的图标也可以依据自己的需求进行改变。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>原生js实现树形表格</title>
</head>
<style>
    .icon-angle-left:before {
        content: '▶';
    }

    .icon-angle-down:before {
        content: '▼';
    }

    table {
        border-spacing: 0px;
        border-collapse: collapse;
        width: 50%;
        margin: 100px auto;
        border: rgb(221, 221, 221);
    }

    table tbody tr.level_odd{
        background-color: #FFF;
    }

    table tbody tr.level_even{
        background-color:  #f5f5f5;
    }

    .c_pointer{
        cursor: pointer;
    }
</style>

<body>
    <table border>
        <thead>
            <tr>
                <th>科目</th>
                <th>分数</th>
            </tr>
        </thead>
        <tbody id="myTbody"></tbody>
    </table>
    <script>
        // 树形结构层级转换
        treeList = [
            {
                name:'总分',
                grade:935
            },
            {
                name: '主课',
                grade: 375,
                childs: [
                    {
                        name: '语文',
                        grade: 125,
                        childs: [
                            {
                                name: '作文',
                                grade: 55
                            }, {
                                name: '其他',
                                grade: 70
                            }
                        ]
                    },
                    {
                        name: '数学',
                        grade: 125
                    },
                    {
                        name: '英语',
                        grade: 125
                    }
                ]
            },
            {
                name: '文综',
                grade: 290,
                childs: [
                    {
                        name: '历史',
                        grade: 100,
                    },
                    {
                        name: '政治',
                        grade: 95
                    },
                    {
                        name: '地理',
                        grade: 95
                    }
                ]
            },
            {
                name: '理综',
                grade: 270,
                childs: [
                    {
                        name: '物理',
                        grade: 80
                    },
                    {
                        name: '生物',
                        grade: 100
                    },
                    {
                        name: '化学',
                        grade: 90
                    }
                ]
            }
        ]
        const fIdChildMapList = {}
        const trStatus = {}
        function flatAndaddSpace(tree, pre = '', idPre = '', pid = '', plevel = 0) {
            let data = []
            const len = tree.length
            for (let i = 0; i < len; i++) {
                if (fIdChildMapList[pid]) {
                    fIdChildMapList[pid].push(idPre + i)
                } else {
                    fIdChildMapList[pid] = [idPre + i]
                }
                tree[i].preSpace = pre
                tree[i].f_id = idPre + i
                tree[i].level = plevel + 1
                trStatus[idPre + i] = true
                const obj = {}
                Object.keys(tree[i]).forEach(key => {
                    if (key != 'childs') {
                        obj[key] = tree[i][key] === null ? '-' : tree[i][key]
                    }
                })
                data.push(obj)
                if (tree[i].childs) {
                    let currentPre = pre + '&nbsp;&nbsp;'
                    data = data.concat(flatAndaddSpace(tree[i].childs, currentPre, tree[i].f_id + '-', tree[i].f_id, tree[i].level))
                }
            }
            return data
        }

        const handleData = flatAndaddSpace(treeList, '', '', '-1')
        /**
        * 
        * @param {string} curId 
        * @param {boolean} showChild 
        * @returns 
        */
        function getWillHideFId(curId) {
            if (!fIdChildMapList[curId]) return []
            var rst = fIdChildMapList[curId]
            fIdChildMapList[curId].forEach(key => {
                rst = rst.concat(getWillHideFId(key))
            })
            return rst
        }

        const createTr = data => {
            const tr = document.createElement('tr')
            let level = data.level % 2 !== 0 ? 'odd' : 'even'
            let icon = fIdChildMapList[data.f_id] ? '<i class="icon icon-angle-left"></i>' : '<i class="icon none"></i>'
            tr.className = `f_id_${data.f_id} level_${level} tree_table_tr ${fIdChildMapList[data.f_id] ?'c_pointer':''}`
            tr.setAttribute('f_id', data.f_id)
            const str = `
            <td>
                ${data.preSpace}
                ${icon}
                ${data.name}
            </td>
            <td>${data.grade}</td>`
            tr.innerHTML = str
            tr.addEventListener('click', function () {
                handleClick(this)
            })
            return tr
        }

        function init() {
            const myTbody = document.querySelector('#myTbody')
            handleData.forEach(item => {
                if (fIdChildMapList['-1'].includes(item.f_id)) {
                    myTbody.appendChild(createTr(item))
                }
            })
        }
        init()

        /**
         * @param {HTMLElement} ele 
         */
        function handleClick(ele) {
            const fid = ele.getAttribute('f_id')
            const showChild = trStatus[fid]
            handShowAndHide(fid, showChild, ele)
            trStatus[fid] = !showChild
            let iconClass = showChild ? "icon icon-angle-down" : "icon icon-angle-left"
            if (ele.firstElementChild.firstElementChild.className.indexOf('none') === -1) {
                ele.firstElementChild.firstElementChild.className = iconClass
                ele.firstElementChild.firstElementChild.className = iconClass

            }
        }

        function handShowAndHide(fid, showChild, ele) {
            if (!showChild) {
                // 关闭
                getWillHideFId(fid).forEach(id => {
                    if (document.querySelector(`.f_id_${id}`))
                        document.querySelector(`.f_id_${id}`).style.display = "none"
                    let className = fIdChildMapList[id] ? "icon icon-angle-left" : "icon none"
                    document.querySelector(`.f_id_${id}`).firstElementChild.firstElementChild.className = className
                    trStatus[id] = false
                })
            } else {
                (fIdChildMapList[fid] || []).forEach(id => {
                    if (document.querySelector(`.f_id_${id}`)) {
                        document.querySelector(`.f_id_${id}`).style.display = "table-row"
                    } else {
                        const tr = createTr(handleData.filter(item => item.f_id === id)[0])
                        document.querySelector('#myTbody').insertBefore(tr, ele.nextSibling)
                    }
                    trStatus[id] = true
                })
            }
        }
    </script>
</body>

</html>
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值