• 原生 JS 实现树组件


      效果:

      使用原生 JS 实现一个简单的树插件。

      首先元数据的数据结构,每个节点需要包含自己的主键和父级节点的主键,才能描述树形结构。比如:

           [{
                    "id": "1",
                    "name": "一级目录1",
                    "fatherId": "0",
                },
                {
                    "id": "2",
                    "name": "二级目录1",
                    "fatherId": "1"
                },
                {
                    "id": "3",
                    "name": "二级目录2",
                    "fatherId": "1"
                },
                {
                    "id": "4",
                    "name": "三级目录1",
                    "fatherId": "2"
                },
                {
                    "id": "5",
                    "name": "三级目录2",
                    "fatherId": "2"
                },
                {
                    "id": "6",
                    "name": "一级目录2",
                    "fatherId": "0"
                },
                {
                    "id": "7",
                    "name": "三极目录3",
                    "fatherId": "3"
                },
                {
                    "id": "8",
                    "name": "四级目录1",
                    "fatherId": "7"
                },
                {
                    "id": "9",
                    "name": "五级目录1",
                    "fatherId": "8"
                }
            ]

       处理该类数据,处理为树形数据结构,方便渲染:

                // 将数组初始化为树结构
                initTreeData: function (arr) {
                    for (let i = 0; i < arr.length; i++) this.nodesMap[arr[i].id] = arr[i];
                    let reArr = [];
                    for (let i = 0; i < arr.length; i++) {
                        arr[i]['showChilds'] = true;
                        if (!this.nodesMap[arr[i].fatherId]) reArr.push(arr[i]);
                        else {
                            let fatherNode = this.nodesMap[arr[i].fatherId];
                            fatherNode.childs = fatherNode.childs || [];
                            fatherNode.childs.push(arr[i]);
                        }
                    }
                    return reArr;
                },

      处理后的数据结构如下:

    [
        {
            "id": "1",
            "name": "一级目录1",
            "fatherId": "0",
            "showChilds": true,
            "childs": [
                {
                    "id": "2",
                    "name": "二级目录1",
                    "fatherId": "1",
                    "showChilds": true,
                    "childs": [
                        {
                            "id": "4",
                            "name": "三级目录1",
                            "fatherId": "2",
                            "showChilds": true
                        },
                        {
                            "id": "5",
                            "name": "三级目录2",
                            "fatherId": "2",
                            "showChilds": true
                        }
                    ]
                },
                {
                    "id": "3",
                    "name": "二级目录2",
                    "fatherId": "1",
                    "showChilds": true,
                    "childs": [
                        {
                            "id": "7",
                            "name": "三极目录3",
                            "fatherId": "3",
                            "showChilds": true,
                            "childs": [
                                {
                                    "id": "8",
                                    "name": "四级目录1",
                                    "fatherId": "7",
                                    "showChilds": true,
                                    "childs": [
                                        {
                                            "id": "9",
                                            "name": "五级目录1",
                                            "fatherId": "8",
                                            "showChilds": true
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        {
            "id": "6",
            "name": "一级目录2",
            "fatherId": "0",
            "showChilds": true
        }
    ]

      将数据渲染到页面上:

           // 根据数组渲染树
                createDom: function (data) {
                    let self = this;
                    let fatherDom = document.getElementById(`${this.rootId}-my-tree-${data.fatherId}`);
                    // 虚拟根节点
                    if (!fatherDom) fatherDom = document.getElementById(this.rootId);
                    let dom = document.createElement("div");
                    dom.id = `${this.rootId}-my-tree-${data.id}`;
                    dom.className = 'treeItem';
                    let iconDom = document.createElement("div");
                    iconDom.id = `${this.rootId}-my-tree-icon-${data.id}`;
                    iconDom.className = "itemIcon";
                    iconDom.innerHTML = "-";
                    // if (data.showChilds) iconDom.innerHTML = "-";
                    // else iconDom.innerHTML = "+";
                    dom.appendChild(iconDom);
                    dom.innerHTML += data.name;
                    fatherDom.appendChild(dom);
                    iconDom = document.getElementById(`${this.rootId}-my-tree-icon-${data.id}`);
                    iconDom.onclick = function () {
                        self.iconClickHandler(iconDom);
                    };
                    // 递归渲染子树
                    if (data.childs && data.childs.length > 0) {
                        for (let i = 0; i < data.childs.length; i++) this.createDom(data.childs[i]);
                    }
                },

      折叠、展开子树:

          iconClickHandler(dom) {
                    if (!dom || !dom.id) return;
                    let id = dom.id;
                    if (id.indexOf("my-tree-icon-") == -1) return;
                    id = id.substring(id.indexOf("my-tree-icon-") + 13);
                    let node = this.nodesMap[id];
                    if (!node) return;
                    if (node.showChilds) {
                        this.hideHandler(dom);
                        dom.innerHTML = "+";
                    } else {
                        this.showHandler(dom);
                        dom.innerHTML = "-";
                    }
                },
    
                // 隐藏子树
                hideHandler: function (dom) {
                    if (!dom || !dom.id) return;
                    let id = dom.id;
                    if (id.indexOf("my-tree-icon-") == -1) return;
                    id = id.substring(id.indexOf("my-tree-icon-") + 13);
                    let node = this.nodesMap[id];
                    if (!node) return;
                    let childs = node.childs;
                    if (!childs) return;
                    node.showChilds = false;
                    for (let i = 0; i < childs.length; i++) {
                        let childDom = document.getElementById(`${this.rootId}-my-tree-${childs[i].id}`);
                        if (!childDom) continue;
                        childDom.style.display = 'none';
                        let childIconDom = document.getElementById(`${this.rootId}-my-tree-icon-${childs[i].id}`);
                        this.hideHandler(childIconDom);
                    }
                },
    
                // 渲染子树
                showHandler: function (dom) {
                    if (!dom || !dom.id) return;
                    let id = dom.id;
                    if (id.indexOf("my-tree-icon-") == -1) return;
                    id = id.substring(id.indexOf("my-tree-icon-") + 13);
                    let node = this.nodesMap[id];
                    if (!node) return;
                    let childs = node.childs;
                    if (!childs) return;
                    node.showChilds = true;
                    for (let i = 0; i < childs.length; i++) {
                        let childDom = document.getElementById(`${this.rootId}-my-tree-${childs[i].id}`);
                        if (!childDom) continue;
                        childDom.style.display = 'block';
                        let childIconDom = document.getElementById(`${this.rootId}-my-tree-icon-${childs[i].id}`);
                        let childNode = this.nodesMap[childs[i].id];
                        childIconDom.innerHTML = "+";
                        // 递归渲染子树
                        this.hideHandler(childIconDom);
                    }
                }

      完整组件代码:

        function MyTree(list, rootId) {
                // 简单的深拷贝
                this.list = JSON.parse(JSON.stringify(list));
                this.rootId = rootId;
                this.nodesMap = {};
                this.treeData = [];
            }
    
            MyTree.prototype = {
                init: function () {
                    this.treeData = this.initTreeData(this.list);
                    for (let i = 0; i < this.treeData.length; i++) {
                        this.createDom(this.treeData[i]);
                    }
                },
    
                // 将数组初始化为树结构
                initTreeData: function (arr) {
                    for (let i = 0; i < arr.length; i++) this.nodesMap[arr[i].id] = arr[i];
                    let reArr = [];
                    for (let i = 0; i < arr.length; i++) {
                        arr[i]['showChilds'] = true;
                        if (!this.nodesMap[arr[i].fatherId]) reArr.push(arr[i]);
                        else {
                            let fatherNode = this.nodesMap[arr[i].fatherId];
                            fatherNode.childs = fatherNode.childs || [];
                            fatherNode.childs.push(arr[i]);
                        }
                    }
                    return reArr;
                },
    
                // 根据数组渲染树
                createDom: function (data) {
                    let self = this;
                    let fatherDom = document.getElementById(`${this.rootId}-my-tree-${data.fatherId}`);
                    // 虚拟根节点
                    if (!fatherDom) fatherDom = document.getElementById(this.rootId);
                    let dom = document.createElement("div");
                    dom.id = `${this.rootId}-my-tree-${data.id}`;
                    dom.className = 'treeItem';
                    let iconDom = document.createElement("div");
                    iconDom.id = `${this.rootId}-my-tree-icon-${data.id}`;
                    iconDom.className = "itemIcon";
                    iconDom.innerHTML = "-";
                    // if (data.showChilds) iconDom.innerHTML = "-";
                    // else iconDom.innerHTML = "+";
                    dom.appendChild(iconDom);
                    dom.innerHTML += data.name;
                    fatherDom.appendChild(dom);
                    iconDom = document.getElementById(`${this.rootId}-my-tree-icon-${data.id}`);
                    iconDom.onclick = function () {
                        self.iconClickHandler(iconDom);
                    };
                    // 递归渲染子树
                    if (data.childs && data.childs.length > 0) {
                        for (let i = 0; i < data.childs.length; i++) this.createDom(data.childs[i]);
                    }
                },
    
                iconClickHandler(dom) {
                    if (!dom || !dom.id) return;
                    let id = dom.id;
                    if (id.indexOf("my-tree-icon-") == -1) return;
                    id = id.substring(id.indexOf("my-tree-icon-") + 13);
                    let node = this.nodesMap[id];
                    if (!node) return;
                    if (node.showChilds) {
                        this.hideHandler(dom);
                        dom.innerHTML = "+";
                    } else {
                        this.showHandler(dom);
                        dom.innerHTML = "-";
                    }
                },
    
                // 隐藏子树
                hideHandler: function (dom) {
                    if (!dom || !dom.id) return;
                    let id = dom.id;
                    if (id.indexOf("my-tree-icon-") == -1) return;
                    id = id.substring(id.indexOf("my-tree-icon-") + 13);
                    let node = this.nodesMap[id];
                    if (!node) return;
                    let childs = node.childs;
                    if (!childs) return;
                    node.showChilds = false;
                    for (let i = 0; i < childs.length; i++) {
                        let childDom = document.getElementById(`${this.rootId}-my-tree-${childs[i].id}`);
                        if (!childDom) continue;
                        childDom.style.display = 'none';
                        let childIconDom = document.getElementById(`${this.rootId}-my-tree-icon-${childs[i].id}`);
                        this.hideHandler(childIconDom);
                    }
                },
    
                // 渲染子树
                showHandler: function (dom) {
                    if (!dom || !dom.id) return;
                    let id = dom.id;
                    if (id.indexOf("my-tree-icon-") == -1) return;
                    id = id.substring(id.indexOf("my-tree-icon-") + 13);
                    let node = this.nodesMap[id];
                    if (!node) return;
                    let childs = node.childs;
                    if (!childs) return;
                    node.showChilds = true;
                    for (let i = 0; i < childs.length; i++) {
                        let childDom = document.getElementById(`${this.rootId}-my-tree-${childs[i].id}`);
                        if (!childDom) continue;
                        childDom.style.display = 'block';
                        let childIconDom = document.getElementById(`${this.rootId}-my-tree-icon-${childs[i].id}`);
                        let childNode = this.nodesMap[childs[i].id];
                        childIconDom.innerHTML = "+";
                        // 递归渲染子树
                        this.hideHandler(childIconDom);
                    }
                }
            }

      测试 html:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>MyTree</title>
        <style>
            .treeItem {
                padding-left: 20px;
                margin: 16px;
            }
    
            .itemIcon {
                display: inline-block;
                width: 18px;
                height: 18px;
                line-height: 16px;
                color: white;
                font-weight: bolder;
                text-align: center;
                font-size: 16px;
                border-radius: 50%;
                background: rgb(85, 141, 213);
                margin-right: 10px;
                cursor: pointer;
            }
        </style>
    </head>
    
    <body>
        <div style="30%;border:0.1px;margin:1%;display: inline-block;position: absolute;left: 0px;">
            <!-- 虚拟根节点 -->
            <div id="my-tree-root0" class="treeItem">虚拟根节点0</div>
        </div>
        <div style="30%;border:0.1px;margin:1%;display: inline-block;height:100vh;position: absolute;left: 30%;">
            <!-- 虚拟根节点 -->
            <div id="my-tree-root1" class="treeItem">虚拟根节点1</div>
        </div>
        <div style="30%;border:0.1px;margin:1%;display: inline-block;position: absolute;left: 60%;">
            <!-- 虚拟根节点 -->
            <div id="my-tree-root2" class="treeItem">虚拟根节点2</div>
        </div>
    
        <script>
            var list = [{
                    "id": "1",
                    "name": "一级目录1",
                    "fatherId": "0",
                },
                {
                    "id": "2",
                    "name": "二级目录1",
                    "fatherId": "1"
                },
                {
                    "id": "3",
                    "name": "二级目录2",
                    "fatherId": "1"
                },
                {
                    "id": "4",
                    "name": "三级目录1",
                    "fatherId": "2"
                },
                {
                    "id": "5",
                    "name": "三级目录2",
                    "fatherId": "2"
                },
                {
                    "id": "6",
                    "name": "一级目录2",
                    "fatherId": "0"
                },
                {
                    "id": "7",
                    "name": "三极目录3",
                    "fatherId": "3"
                },
                {
                    "id": "8",
                    "name": "四级目录1",
                    "fatherId": "7"
                },
                {
                    "id": "9",
                    "name": "五级目录1",
                    "fatherId": "8"
                }
            ];
    
            function MyTree(list, rootId) {
                // 简单的深拷贝
                this.list = JSON.parse(JSON.stringify(list));
                this.rootId = rootId;
                this.nodesMap = {};
                this.treeData = [];
            }
    
            MyTree.prototype = {
                init: function () {
                    this.treeData = this.initTreeData(this.list);
                    for (let i = 0; i < this.treeData.length; i++) {
                        this.createDom(this.treeData[i]);
                    }
                },
    
                // 将数组初始化为树结构
                initTreeData: function (arr) {
                    for (let i = 0; i < arr.length; i++) this.nodesMap[arr[i].id] = arr[i];
                    let reArr = [];
                    for (let i = 0; i < arr.length; i++) {
                        arr[i]['showChilds'] = true;
                        if (!this.nodesMap[arr[i].fatherId]) reArr.push(arr[i]);
                        else {
                            let fatherNode = this.nodesMap[arr[i].fatherId];
                            fatherNode.childs = fatherNode.childs || [];
                            fatherNode.childs.push(arr[i]);
                        }
                    }
                    return reArr;
                },
    
                // 根据数组渲染树
                createDom: function (data) {
                    let self = this;
                    let fatherDom = document.getElementById(`${this.rootId}-my-tree-${data.fatherId}`);
                    // 虚拟根节点
                    if (!fatherDom) fatherDom = document.getElementById(this.rootId);
                    let dom = document.createElement("div");
                    dom.id = `${this.rootId}-my-tree-${data.id}`;
                    dom.className = 'treeItem';
                    let iconDom = document.createElement("div");
                    iconDom.id = `${this.rootId}-my-tree-icon-${data.id}`;
                    iconDom.className = "itemIcon";
                    iconDom.innerHTML = "-";
                    // if (data.showChilds) iconDom.innerHTML = "-";
                    // else iconDom.innerHTML = "+";
                    dom.appendChild(iconDom);
                    dom.innerHTML += data.name;
                    fatherDom.appendChild(dom);
                    iconDom = document.getElementById(`${this.rootId}-my-tree-icon-${data.id}`);
                    iconDom.onclick = function () {
                        self.iconClickHandler(iconDom);
                    };
                    // 递归渲染子树
                    if (data.childs && data.childs.length > 0) {
                        for (let i = 0; i < data.childs.length; i++) this.createDom(data.childs[i]);
                    }
                },
    
                iconClickHandler(dom) {
                    if (!dom || !dom.id) return;
                    let id = dom.id;
                    if (id.indexOf("my-tree-icon-") == -1) return;
                    id = id.substring(id.indexOf("my-tree-icon-") + 13);
                    let node = this.nodesMap[id];
                    if (!node) return;
                    if (node.showChilds) {
                        this.hideHandler(dom);
                        dom.innerHTML = "+";
                    } else {
                        this.showHandler(dom);
                        dom.innerHTML = "-";
                    }
                },
    
                // 隐藏子树
                hideHandler: function (dom) {
                    if (!dom || !dom.id) return;
                    let id = dom.id;
                    if (id.indexOf("my-tree-icon-") == -1) return;
                    id = id.substring(id.indexOf("my-tree-icon-") + 13);
                    let node = this.nodesMap[id];
                    if (!node) return;
                    let childs = node.childs;
                    if (!childs) return;
                    node.showChilds = false;
                    for (let i = 0; i < childs.length; i++) {
                        let childDom = document.getElementById(`${this.rootId}-my-tree-${childs[i].id}`);
                        if (!childDom) continue;
                        childDom.style.display = 'none';
                        let childIconDom = document.getElementById(`${this.rootId}-my-tree-icon-${childs[i].id}`);
                        this.hideHandler(childIconDom);
                    }
                },
    
                // 渲染子树
                showHandler: function (dom) {
                    if (!dom || !dom.id) return;
                    let id = dom.id;
                    if (id.indexOf("my-tree-icon-") == -1) return;
                    id = id.substring(id.indexOf("my-tree-icon-") + 13);
                    let node = this.nodesMap[id];
                    if (!node) return;
                    let childs = node.childs;
                    if (!childs) return;
                    node.showChilds = true;
                    for (let i = 0; i < childs.length; i++) {
                        let childDom = document.getElementById(`${this.rootId}-my-tree-${childs[i].id}`);
                        if (!childDom) continue;
                        childDom.style.display = 'block';
                        let childIconDom = document.getElementById(`${this.rootId}-my-tree-icon-${childs[i].id}`);
                        let childNode = this.nodesMap[childs[i].id];
                        childIconDom.innerHTML = "+";
                        // 递归渲染子树
                        this.hideHandler(childIconDom);
                    }
                }
            }
    
            var myTree0 = new MyTree(list, "my-tree-root0");
            myTree0.init();
            var myTree1 = new MyTree(list, "my-tree-root1");
            myTree1.init();
            var myTree2 = new MyTree(list, "my-tree-root2");
            myTree2.init();
        </script>
    </body>
    
    </html>

      

  • 相关阅读:
    字符编码 乱码问题
    Django ORM那些相关操作
    pymysql模块使用---Python连接MySQL数据库
    数据库MySQL 之 索引原理与慢查询优化
    数据库MySQL之 视图、触发器、存储过程、函数、事务、数据库锁、数据库备份、事件
    数据库 MySQL 之 数据操作
    数据库 MySQL 之 表操作、存储引擎
    [BZOJ 4212]神牛的养成计划(Trie+可持久化Trie)
    [LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)
    [BZOJ 2865]字符串识别(后缀数组+线段树)
  • 原文地址:https://www.cnblogs.com/niuyourou/p/16335498.html
Copyright © 2020-2023  润新知