突破编程_前端_JS编程实例(简单树结构组件)

news2025/1/21 18:54:49

1 开发目标

实现如下简单树结构组件:

在这里插入图片描述

再点击树节点后,会调用客户端传入的回调函数:

在这里插入图片描述

2 详细需求

简单树结构组件需根据客户端提供的参数创建,具备动态构建树形结构节点、选项卡切换及自定义内容显示等功能:

(1)树形结构组件的创建与初始化: 类似于 echarts 等知名商业组件的创建与初始化方式,本组件需要根据客户端提供的参数 container 以及 para 进行创建和初始化。
container 是一个已存在的 DOM 元素(一般是 DIV),组件将在此元素内部构建 TAB 区域,包含选项卡以及 TAB 面板。
para 是本组件的配置参数,该对象应包含以下属性:

{
	"nodes":[
		{
			"id":"1",
			"name":"node_1",
			"children":[
				{
					"id":"11",
					"name":"node_1_1",
				},
				{
					"id":"12",
					"name":"node_1_2",
					"children":[
						{
							"id":"121",
							"name":"node_1_2_1",
						},
						{
							"id":"122",
							"name":"node_1_2_2",
						},
					]
				},
			]
		},
		{
			"id":"2",
			"name":"node_2",
		},
	],
	"onClickTreeNode":callback_function,
}

(2)树形结构渲染: 组件应能正确渲染树形结构,每个节点应清晰展示。

(3)节点单选: 用户点击树形结构中的某个节点时,该节点应被选中,同时其他节点应取消选中状态。

(4)节点状态反馈: 选中的节点应有明显的视觉反馈,如变色或添加特殊标记。

(5)事件监听: 应提供事件监听机制,允许用户监听节点的选中事件,以便在节点被选中时执行特定操作。

3 代码实现

首先创建一个 neat_treewidget.js 文件,该文件用于本组件的工具类、窗体部件基类以及各个实现类的代码构建。

在具体的业务代码编写之前,先实现一个工具类以及一些工具方法,方便后面调用:

class CommonUtil {

    // 设置 DIV 中的文字为水平与垂直居中
    static centerTextInDiv(container) {
        container.style.display = 'flex';
        container.style.textAlign = 'center';
        container.style.justifyContent = 'center';
        container.style.flexDirection = 'column';
    }
}

该工具类中包含一个可以将 DIV 中的文字设置为水平与垂直居中的静态方法。

接下来,定义一个通用的显示窗体的基类:

class NeatBaseWid {
    constructor(container, para) {
        this.container = container;	// 接收调用者传入的 DOM 元素(一般是 DIV)
        this.para = para;	// 保存调用者传入的 para 对象
    }
}

然后开始定义树节点类型:

class NeatTreeNode extends NeatBaseWid {

    static LEVEL_OFFSET = 10;     // 每个级别的树节点偏移像素
    static NODE_HEIGHT = '23px';     // 树节点高度
    static NODE_NAME_FONTSIZE = '14px';     // 默认标题字符串的字体大小
    static NODE_NAME_COLOR = '#000';     // 默认标题字符串字体颜色
    static NODE_OPENCLOSE_ICON_WIDTH = '23px';     // 树节点打开、关闭小箭头图标的宽度
    static NODE_OPENCLOSE_ICON_CLASS_OPEN = 'fa fa-angle-down';			//打开
    static NODE_OPENCLOSE_ICON_CLASS_CLOSE = 'fa fa-angle-right';		//关闭

    constructor(container, para) {
        super(container, para);
        this.id=this.para.id;
        this.name=this.para.name;
        this.parent = para.treeNode ?? null;        // 父节点
        this.level = (para.treeNode && (para.treeNode.level + 1)) ?? 0;   // 节点的级别,最高一级是根节点
        this.nameContainer = null; // 树节点的标题容器
        this.openCloseIconContainer = null; // 树节点的打开、关闭小箭头图标容器
        this.children = [];       // 子节点
        this.childrenContainer = null; // 子节点容器
        this.render();
    }

上面代码定义了 NeatTreeNode 的一些默认属性与成员变量,并且创建构造函数,该函数接收调用者传入的 DIV 容器,并且调用 render 方法。
在 render 方法,需要渲染当前树节点,并且还要根据是否有子节点的情况,创建子节点容器:

	render() {
        this.container.innerHTML = '';  // 清空容器

        // 渲染当前树节点
        let nodeContainer = document.createElement('div');
        nodeContainer.style.display = 'flex';
        nodeContainer.style.width = '100%';
        nodeContainer.style.height = NeatTreeNode.NODE_HEIGHT;
        this.container.appendChild(nodeContainer);

上面代码创建了当前树节点的容器,接下来需要根据当前节点的级别,做一个偏移处理:

		// 如果当前树节点的级别大于 0,则放一个偏移容器
        if (this.level > 0) {
            let offsetContainer = document.createElement('div');
            offsetContainer.style.width = this.level*NeatTreeNode.LEVEL_OFFSET + 'px';
            nodeContainer.appendChild(offsetContainer);
        }

接下来,如果当前树节点有子节点,则显示树节点的打开、关闭小箭头图标,如果没有子节点,则做一个简单的偏移处理即可:

		// 如果当前树节点有子节点,则显示树节点的打开、关闭小箭头图标
        if (this.para.children && this.para.children.length > 0) {
            this.openCloseIconContainer = document.createElement('i');
            this.openCloseIconContainer.className = NeatTreeNode.NODE_OPENCLOSE_ICON_CLASS_OPEN;
            this.openCloseIconContainer.style.width = NeatTreeNode.NODE_OPENCLOSE_ICON_WIDTH;
            this.openCloseIconContainer.style.height = '100%';
            CommonUtil.centerTextInDiv(this.openCloseIconContainer);
            nodeContainer.appendChild(this.openCloseIconContainer);
            
            // 树节点打开或关闭
            let that = this;
            this.openCloseIconContainer.onclick = function () {
                if(NeatTreeNode.NODE_OPENCLOSE_ICON_CLASS_OPEN === that.openCloseIconContainer.className){
                    that.openCloseIconContainer.className = NeatTreeNode.NODE_OPENCLOSE_ICON_CLASS_CLOSE;
                    that.childrenContainer.style.display='none';
                }else{
                    that.openCloseIconContainer.className = NeatTreeNode.NODE_OPENCLOSE_ICON_CLASS_OPEN;
                    that.childrenContainer.style.display='block';
                }
            }
        } else {
            let offsetContainer = document.createElement('div');
            offsetContainer.style.width = NeatTreeNode.NODE_OPENCLOSE_ICON_WIDTH;
            nodeContainer.appendChild(offsetContainer);
        }

注意:上面代码中,在创建打开、关闭小箭头图标后,还定义了点击事件的处理函数。
然后,创建当前的树节点标题,并且定义点击树节点标题时的事件处理函数:

        // 树节点标题
		this.nameContainer = document.createElement('div');
        this.nameContainer.style.flexGrow = '1';
        this.nameContainer.innerText = this.para.name;
        CommonUtil.centerTextInDiv(this.nameContainer);
        this.nameContainer.style.textAlign = 'left';
        this.nameContainer.style.cursor = 'pointer';
        nodeContainer.appendChild(this.nameContainer);
		
        // 点击树节点的触发动作
        let that = this;
        this.nameContainer.onclick = function () {
            that.para.onClickTreeNode(that);
        }

最后,根据是否有子节点的情况,创建子节点容器:

        // 创建子树节点
        if(this.para.children && this.para.children.length > 0){
            this.childrenContainer = document.createElement('div');
            this.childrenContainer.style.width = '100%';
            this.container.appendChild(this.childrenContainer);
            this.para.children.forEach(element => {
                let nodeContainer = document.createElement('div');
                this.childrenContainer.appendChild(nodeContainer);
                element.treeNode = this;
                element.onClickTreeNode = this.para.onClickTreeNode;
                let treeNode = new NeatTreeNode(nodeContainer, element);
                this.children.push(treeNode);
            });
        }
	}

至此,完成了整个渲染函数 render 的构建。

下一步,创建树结构类型:

class NeatHeaderTreeWidget extends NeatBaseWid {

    constructor(container, para) {
        super(container, para);
        this.rootTreeNodes = [];      // 根节点集合
        this.render();
    }

    render() {
        // 清空容器
        this.container.innerHTML = '';

        // 渲染树结构
        this.container.style.width = '100%';
        this.container.style.height = '100%';

        // 创建树节点
        this.para.nodes.forEach(element => {
            let nodeContainer = document.createElement('div');
            this.container.appendChild(nodeContainer);
            element.onClickTreeNode = this.para.onClickTreeNode;
            let treeNode = new NeatTreeNode(nodeContainer, element);
            this.rootTreeNodes.push(treeNode);
        });
    }
}

注意:创建树结构类型的渲染函数 render 中的创建树节点实际是创建根节点,至于子节点则通过根节点的渲染函数创建(是一个递归创建的过程)。

完成树结构组件的代码编写后,可以创建 neater_treewidget.html 文件,调用树结构组件:

<!DOCTYPE html>
<html>

<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>header tab</title>
    <style>
        html {
            height: 100%;
        }

        body {
            margin: 0;
            height: 100%;
        }
    </style>
    <link rel="stylesheet" href="./font-awesome-4.7.0/css/font-awesome.css">
</head>

<body>
    <div id="divMain" style="height: 100%;width: 100%;"></div>
</body>
<script src="./neat_treewidget.js"></script>
<script>
    let para = {
        "nodes": [
            {
                "id": "1",
                "name": "根节点 node_1",
                "children": [
                    {
                        "id": "11",
                        "name": "一级子节点 node_1_1",
                    },
                    {
                        "id": "12",
                        "name": "一级子节点 node_1_2",
                        "children": [
                            {
                                "id": "121",
                                "name": "二级子节点 node_1_2_1",
                            },
                            {
                                "id": "122",
                                "name": "二级子节点 node_1_2_2",
                            },
                        ]
                    },
                ]
            },
            {
                "id": "2",
                "name": "根节点 node_2",
            },
            {
                "id": "3",
                "name": "根节点 node_3",
                "children": [
                    {
                        "id": "31",
                        "name": "一级子节点 node_3_1",
                    },
                ]
            },
            {
                "id": "4",
                "name": "根节点 node_4",
            },
        ],
        "onClickTreeNode":clickTreeNode,
    };

    function clickTreeNode(treeNode){
        alert(treeNode.name);
    }

    let treeWidget = new NeatHeaderTreeWidget(document.getElementById('divMain'), para);

</script>

</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1495741.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

R语言生物群落(生态)数据统计分析与绘图教程

原文链接&#xff1a;R语言生物群落&#xff08;生态&#xff09;数据统计分析与绘图教程 前沿 R 语言作的开源、自由、免费等特点使其广泛应用于生物群落数据统计分析。生物群落数据多样而复杂&#xff0c;涉及众多统计分析方法。 第一 R基础及数据准备 一&#xff1a;R和R…

【树上倍增】【割点】 【换根法】3067. 在带权树网络中统计可连接服务器对数目

作者推荐 视频算法专题 本文涉及知识点 树上倍增 树 图论 并集查找 换根法 深度优先 割点 LeetCode3067. 在带权树网络中统计可连接服务器对数目 给你一棵无根带权树&#xff0c;树中总共有 n 个节点&#xff0c;分别表示 n 个服务器&#xff0c;服务器从 0 到 n - 1 编号…

快速了解Redis

Redis是什么&#xff1f; Redis是一个数据库&#xff0c;是一个跨平台的非关系型数据库&#xff0c;Redis完全开源&#xff0c;遵守BSD协议。它通过键值对(Key-Value)的形式存储数据。 它与mysql数据库有什么区别&#xff1f; redis通过键值对(Key-Value)的形式存储数据&…

深入理解 Vuex:从基础到应用场景

前言 在之前的文章中&#xff0c;我们已经对 Vue.js 有了一定的了解。今天我们要对Vue官方的状态共享管理器Vuex进行详细讲解&#xff0c;将其基本吃透&#xff0c;目标是面对大多数业务需求&#xff1b; 一、介绍 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用…

Rust入门:GCC或VS2019中的c或c++程序如何调用Rust静态库

首先创建一个rust的库&#xff0c;这里我假设命名为c-to-rust1 cargo new --lib c-to-rust1 其中&#xff0c;src/lib.rs的内容如下&#xff0c; #[no_mangle] pub extern "C" fn get_string() -> *const u8 {b"Hello C World\0".as_ptr() }注解 …

高分辨率全球海洋温度和盐度再分析数据Global Ocean Physics Reanalysis(0.083°),并利用matlab读取绘图

1.引言 在研究全球海平面变化的问题中&#xff0c;卫星测高获得总的海平面变化&#xff0c;而海平面变化包含质量变化和比容变化。因此测高数据和海洋物理分析数据对于海平面研究至关重要。 测高数据下载网址&#xff1a; Global Ocean Gridded L 4 Sea Surface Heights And …

【深度学习笔记】计算机视觉——FCN(全卷积网络

全卷积网络 sec_fcn 如 :numref:sec_semantic_segmentation中所介绍的那样&#xff0c;语义分割是对图像中的每个像素分类。 全卷积网络&#xff08;fully convolutional network&#xff0c;FCN&#xff09;采用卷积神经网络实现了从图像像素到像素类别的变换 :cite:Long.Sh…

Docker数据卷的挂载

目录 1 概念 2 常用命令 3 操作步骤(主要讲在创建容器时的挂载) 3.1 挂载在默认目录 3.2 挂载在自定义目录 4 附加内容(查看容器的挂载情况) 1 概念 数据卷&#xff08;volume&#xff09;是一个虚拟目录&#xff0c;是容器内目录与宿主机目录之间映射的桥梁。这样容器内…

如何恢复已删除的华为手机图片?5 种方式分享

不幸的现实是&#xff0c;华为的珍贵时刻有时会因为意外删除、软件故障或其他不可预见的情况而在眨眼之间消失。在这种情况下&#xff0c;寻求恢复已删除的图片成为个人迫切关心的问题。 本文旨在为用户提供如何从华为恢复已删除图片的实用解决方案。我们将探索五种可行的方法…

#微信小程序创建(获取onenet平台数据)

1.IDE&#xff1a;微信开发者工具 2.实验&#xff1a;创建一个小程序&#xff08;http get获取onenet平台数据&#xff09; 3.记录&#xff1a; 百度网盘链接&#xff1a;https://pan.baidu.com/s/1eOd-2EnilnhPWoGUMj0fzw 提取码: 2023 &#xff08;1&#xff09;新建一个工…

CentOS/Fedora/Ubuntu/Debian 系统 wget 命令

wget 是云服务器安装环境和面板常用下载命令。下载软件或从远程服务器下载备份到本地服务器&#xff0c;也可以使用 wget 把文件下载到云服务器上。 VPS wget 命令最常用使用方法如下&#xff1a; 安装 wget 一般来说 wget 命令是系统自带的&#xff0c;方面安装环境和面板&…

LVS----DR模式

一、LVS-DR工作原理 1、LVS-DR数据包流向分析 客户端发送请求到Director Server (负载均衡器)&#xff0c;请求的数据报文&#xff08;源IP是CIP&#xff0c;目标IP是VIP&#xff09;到达内核空间。Director Server 和Real Server 在同一个网络中&#xff0c;数据通过二层数据…

guava的使用

对数组操作前判断是否会越界&#xff1a; List<String> s new ArrayList<>();System.out.println(Preconditions.checkElementIndex(2,s.size(),"下标长度超过了")); 是否为空 String s null;System.out.println(Preconditions.checkNotNull(s)); 判空…

六自由度Stewart平台的matlab模拟与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1运动学原理 4.2 Stewart平台运动学方程 5.完整工程文件 1.课题概述 六自由度Stewart平台的matlab模拟与仿真&#xff0c;模拟六自由度Stewart平台的动态变化情况以及伺服角度。 2.系统仿真结果 3.核…

设置word目录从正文开始记录页码,并解决word目录正常,但正文页脚处只显示第一页的页码

设置word目录从正文开始记录页码&#xff0c;并解决word目录正常&#xff0c;但正文页脚处只显示第一页的页码 问题详情1&#xff1a;如何设置目录从正文开始记录页码 问题详情2&#xff1a;word目录处的页码正常&#xff0c;但正文只有第一页的页脚处显示页码 解决方法 在设置…

SpringCloud微服务-RabbitMQ快速入门

文章目录 RabbitMQ快速入门1、什么是MQ&#xff1f;2、RabbitMQ概述3、RabbitMQ的结构和概念4、常见消息模型5、HelloWorld RabbitMQ快速入门 1、什么是MQ&#xff1f; MQ &#xff08;MessageQueue&#xff09;&#xff0c;中文是消息队列&#xff0c;字面来看就是存放消息的…

【❤️算法笔记❤️】-每日一刷-21、合并两个有序链表

文章目录 题目思路解答 题目 简单 相关标签 相关企业 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入…

蓝桥杯-排序

数组排序 Arrays.sort(int[] a) 这种形式是对一个数组的所有元素进行排序&#xff0c;并且时按从小到大的顺序。 package Work;import java.util.*;public class Imcomplete {public static void main(String args[]) {int arr[]new int [] {1,324,4,5,7,2};Arrays.sort(arr)…

【Java】CAP理论以及它的实际应用案例

目录 简介 不是所谓的“3 选 2” CAP 实际应用案例 总结 CAP 理论/定理起源于 2000年&#xff0c;由加州大学伯克利分校的Eric Brewer教授在分布式计算原理研讨会&#xff08;PODC&#xff09;上提出&#xff0c;因此 CAP定理又被称作 布鲁尔定理&#xff08;Brewer’s the…

JavaSE——基础小项目-模拟ATM系统(项目主要目标、技术选型、架构搭建、具体实现、完整代码注释)

目录 项目主要目标 技术选型 面向对象编程 使用集合容器 程序流程控制 使用常见API 系统架构搭建与欢迎页设计 Account ATM Test 用户开户功能实现 录入账户名称与性别 录入账户密码与取现额度 生成新卡号 存入账户 登录功能实现 登录后操作实现 退出账户 存…