使用 HTML + jsmind 实现在线思维导图

article/2025/6/21 22:10:38

在日常工作和学习中,思维导图是一种非常有效的可视化工具,可以帮助我们梳理思路、规划任务、整理知识结构。本文将带你一步步了解如何使用 HTML 和 jsmind 实现一个基础的在线思维导图应用。

效果演示

image-20250527223000780

image-20250527223032429

项目概述

本项目主要包含以下核心功能:

  • 初始化思维导图
  • 添加子节点、删除节点、编辑节点内容
  • 保存数据到本地存储,刷新页面后数据不丢失
  • 导出思维导图数据为 JSON 文件
  • 导入 JSON 数据
  • 保存为图片

准备工作

下载 jsmind 文件,项目中引入以下主要文件:jsmind.css、jsmind.js、jsmind.draggable-node.js、jsmind.screenshot.js。

jsmind 下载地址:https://github.com/hizzgdev/jsmind

<link type="text/css" rel="stylesheet" href="./jsmind.css" />
<script src="./jsmind.js"></script>
<script src="./jsmind.draggable-node.js"></script>
<script src="./jsmind.screenshot.js"></script>

页面结构与样式设计

创建 HTML 结构
<!-- 工具栏 -->
<div class="toolbar"><button onclick="addNode()">添加子节点</button><button onclick="removeNode()">删除节点</button><button onclick="editNode()">编辑节点</button><button onclick="saveDataToLocal()">保存</button><button onclick="downloadData()">导出</button><button onclick="saveAsImage()">保存为图片</button><input type="file" id="importFile" accept=".json" style="display: none;" onchange="importDataFromFile(event)"><label for="importFile" class="button">导入数据</label>
</div>
<!-- 思维导图容器 -->
<div id="jsmind_container"></div>
设计 CSS 样式
body {margin: 0;padding: 0;
}
#jsmind_container {width: 100vw;height: 100vh;overflow: auto;box-sizing: border-box;top: 0;
}
.toolbar {position: fixed;top: 10px;left: 50%;transform: translateX(-50%);z-index: 1000;display: flex;gap: 10px;
}
.toolbar button,.toolbar .button {padding: 8px 16px;border: none;border-radius: 4px;background-color: #007bff;color: white;cursor: pointer;transition: background-color 0.3s ease;
}
.toolbar button:hover {background-color: #0056b3;
}

核心功能实现

初始化思维导图

使用 jsMind 初始化一个可编辑的思维导图实例,支持从浏览器本地存储(localStorage)中加载上次保存的数据。

// 初始化思维导图
let jm = null;
const initMindMap = () => {const options = {container: 'jsmind_container',editable: true,theme: 'white',};// 初始数据let data = {"meta": {"name": "示例思维导图","author": "user","version": "1.0"},"format": "node_tree","data": {"id": "root","topic": "中心主题","children": []}};const savedData = localStorage.getItem('mindmapData');if (savedData) {try {data = JSON.parse(savedData);} catch (e) {console.error("解析本地数据失败", e);}}jm = new jsMind(options);jm.show(data);
}
window.onload = initMindMap;
添加子节点
function addNode() {const selectedNode = jm.get_selected_node();if (selectedNode) {const nodeId = Date.now().toString();jm.add_node(selectedNode.id, nodeId, '新增子节点', {});}
}
删除节点
function removeNode() {const selectedNode = jm.get_selected_node();if (selectedNode && selectedNode.id !== 'root') {jm.remove_node(selectedNode);}
}
编辑节点
function editNode() {const selectedNode = jm.get_selected_node();if (selectedNode) {const newText = prompt('输入新内容', selectedNode.topic);if (newText) {jm.update_node(selectedNode.id, newText);}}
}
保存数据到本地存储
function saveDataToLocal() {const mindData = jm.get_data();localStorage.setItem('mindmapData', JSON.stringify(mindData));
}
导出思维导图数据

将当前数据保存为 .json 文件并触发下载。

function downloadData() {saveDataToLocal();const mindData = jm.get_data();const dataStr = JSON.stringify(mindData, null, 2);const blob = new Blob([dataStr], {type: 'application/json'});const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = 'mindmap.json';a.click();
}
导入 JSON 数据

可以通过上传已导出的 JSON 文件恢复之前的思维导图结构。

function importDataFromFile(event) {const file = event.target.files[0];if (!file) return;const reader = new FileReader();reader.onload = function(e) {try {const mindData = JSON.parse(e.target.result);jm.show(mindData);alert("导入成功!");} catch (error) {alert("导入失败,请选择有效的JSON文件。");console.error("解析文件失败:", error);}};reader.readAsText(file);
}
保存为图片

利用 jsmind.screenshot.js 插件将整个思维导图截图并保存为图片文件。

function saveAsImage() {saveDataToLocal();jm.screenshot.shootDownload();
}

扩展建议

  • 主题切换:通过修改 jm 的 theme 配置,提供多种预设主题
  • 节点图标与样式设置
  • 快捷键支持

完整代码

<!DOCTYPE html>
<html lang="zh-CN">
<head><title>在线思维导图</title><meta charset="utf-8"><!-- 引入 jsMind 样式 --><link type="text/css" rel="stylesheet" href="./jsmind.css" /><style>body {margin: 0;padding: 0;}#jsmind_container {width: 100vw;height: 100vh;overflow: auto;box-sizing: border-box;top: 0;}.toolbar {position: fixed;top: 10px;left: 50%;transform: translateX(-50%);z-index: 1000;display: flex;gap: 10px;}.toolbar button,.toolbar .button {padding: 8px 16px;border: none;border-radius: 4px;background-color: #007bff;color: white;cursor: pointer;transition: background-color 0.3s ease;}.toolbar button:hover {background-color: #0056b3;}</style>
</head>
<body>
<!-- 工具栏 -->
<div class="toolbar"><button onclick="addNode()">添加子节点</button><button onclick="removeNode()">删除节点</button><button onclick="editNode()">编辑节点</button><button onclick="saveDataToLocal()">保存</button><button onclick="downloadData()">导出</button><button onclick="saveAsImage()">保存为图片</button><input type="file" id="importFile" accept=".json" style="display: none;" onchange="importDataFromFile(event)"><label for="importFile" class="button">导入数据</label>
</div>
<!-- 思维导图容器 -->
<div id="jsmind_container"></div><script src="./jsmind.js"></script>
<script src="./jsmind.draggable-node.js"></script>
<script src="./jsmind.screenshot.js"></script>
<script>// 初始化思维导图let jm = null;const initMindMap = () => {const options = {container: 'jsmind_container',editable: true,theme: 'white',};// 初始数据let data = {"meta": {"name": "示例思维导图","author": "user","version": "1.0"},"format": "node_tree","data": {"id": "root","topic": "中心主题","children": []}};const savedData = localStorage.getItem('mindmapData');if (savedData) {try {data = JSON.parse(savedData);} catch (e) {console.error("解析本地数据失败", e);}}jm = new jsMind(options);jm.show(data);}// 添加子节点function addNode() {const selectedNode = jm.get_selected_node();if (selectedNode) {const nodeId = Date.now().toString();jm.add_node(selectedNode.id, nodeId, '新增子节点', {});}}// 删除节点function removeNode() {const selectedNode = jm.get_selected_node();if (selectedNode && selectedNode.id !== 'root') {jm.remove_node(selectedNode);}}// 编辑节点function editNode() {const selectedNode = jm.get_selected_node();if (selectedNode) {const newText = prompt('输入新内容', selectedNode.topic);if (newText) {jm.update_node(selectedNode.id, newText);}}}// 保存数据到本地存储function saveDataToLocal() {const mindData = jm.get_data();localStorage.setItem('mindmapData', JSON.stringify(mindData));}// 导出数据function downloadData() {saveDataToLocal();const mindData = jm.get_data();const dataStr = JSON.stringify(mindData, null, 2);const blob = new Blob([dataStr], {type: 'application/json'});const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = 'mindmap.json';a.click();}// 导入数据function importDataFromFile(event) {const file = event.target.files[0];if (!file) return;const reader = new FileReader();reader.onload = function(e) {try {const mindData = JSON.parse(e.target.result);jm.show(mindData);alert("导入成功!");} catch (error) {alert("导入失败,请选择有效的JSON文件。");console.error("解析文件失败:", error);}};reader.readAsText(file);}function saveAsImage() {saveDataToLocal();jm.screenshot.shootDownload();}// 初始化window.onload = initMindMap;
</script>
</body>
</html>

http://www.hkcw.cn/article/xcmWIepifv.shtml

相关文章

利用python工具you-get下载网页的视频文件

有时候我们可能在一个网站看到一个视频&#xff08;比如B站&#xff09;&#xff0c;想下载&#xff0c;但是页面没有下载视频的按钮。这时候&#xff0c;我们可以借助python工具you-get来实现下载功能。下面简要说下步骤 &#xff08;一&#xff09;因为使用的是python工具&a…

threejs渲染器和前端UI界面

1. three.js Canvas画布布局 学习本节课之前&#xff0c;可以先回顾下第一章节入门部分的6和12两小节关于threejs Canvas画布布局的讲解。 网页上局部特定尺寸&#xff1a;1.6 第一个3D案例—渲染器(opens new window) 全屏&#xff0c;随窗口变化:1.12 Canvas画布布局和全屏…

嵌入式编译工具链熟悉与游戏移植

在自己的虚拟机Ubuntu系统下&#xff0c;逐步编译 mininim源码(波斯王子重制开源版&#xff09; 指令流程 sudo apt-get remove liballegro5-dev liballegro-image5-dev \liballegro-audio5-dev liballegro-acodec5-dev liballegro-dialog5-dev sudo apt-get install automak…

IEEE P370:用于高达 50 GHz 互连的夹具设计和数据质量公制标准

大多数高频仪器&#xff0c;如矢量网络分析仪 &#xff08;VNA&#xff09; 和时域反射仪 &#xff08;TDR&#xff09;&#xff0c;都可以在同轴接口的末端进行非常好的测量。然而&#xff0c;复杂系统中使用的互连很少具有同轴接口。用于表征这些设备的夹具的设计和实施会对测…

随机响应噪声-极大似然估计

一、核心原因&#xff1a;噪声机制的数学可逆性 在随机响应机制&#xff08;Randomized Response&#xff09;中使用极大似然估计&#xff08;Maximum Likelihood Estimation, MLE&#xff09;是为了从扰动后的噪声数据中无偏地还原原始数据的统计特性。随机响应通过已知概率的…

二叉搜索树——红黑树

红黑树 概念红黑树的原理红黑树的效率红黑树的插入规则变色旋转变色红黑树的验证 代码如下 概念 红黑树本质也是一颗二叉搜索树&#xff0c;他的每个结点增加⼀个存储位来表⽰结点的颜⾊&#xff0c;可以是红⾊或者⿊⾊。通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约…

PCB设计教程【强化篇】——USB拓展坞元件选型

前言 本教程基于B站Expert电子实验室的PCB设计教学的整理&#xff0c;为个人学习记录&#xff0c;旨在帮助PCB设计新手入门。所有内容仅作学习交流使用&#xff0c;无任何商业目的。若涉及侵权&#xff0c;请随时联系&#xff0c;将会立即处理 目录 前言 USB 拓展坞项目概述…

C++11新特性lambda的使用详解

得益于C11的发布&#xff0c;提供了提高效率的语法&#xff0c;C11以后是现代C&#xff0c;C98是传统C&#xff0c;这里来介绍lambda的使用和原理。 目录 1.lambda 2.列表捕捉 3&#xff0c;lambda的应用 4&#xff0c;lambda原理 1.lambda lambda表达式本质是一个匿名函…

4000万日订单背后,饿了么再掀即时零售的“效率革命”

当即时零售转向价值深耕&#xff0c;赢面就是综合实力的强弱。 文&#xff5c;郭梦仪 编&#xff5c;王一粟 在硝烟弥漫的外卖行业“三国杀”中&#xff0c;饿了么与淘宝闪购的日订单量竟然突破了4000万单。 而距淘宝闪购正式上线&#xff0c;还不到一个月。 在大额福利优惠…

PostIn入门教程 - 使用IDEA插件快速生成API接口定义

PostIn是一款国产开源免费的接口管理工具&#xff0c;包含项目管理、接口调试、接口文档设计、接口数据MOCK等模块&#xff0c;支持常见的HTTP协议、websocket协议等。IDEA插件支持扫描代码自动生成接口文档并上传到PostIn系统。本文将详细介绍怎么安装IDEA插件&#xff0c;使用…

在RTX5060Ti上进行Qwen3-4B的GRPO强化微调

导语 最近赶上618活动&#xff0c;将家里的RTX 4060显卡升级为了RTX 5060Ti 16GB版本&#xff0c;显存翻了一番&#xff0c;可以进行一些LLM微调实验了&#xff0c;本篇博客记录使用unsloth框架在RTX 5060Ti 16GB显卡上进行Qwen3-4B-Base模型的GRPO强化微调实验。 简介 GPU性…

用户认证的魔法配方:从模型设计到密码安全的奇幻之旅

title: 用户认证的魔法配方:从模型设计到密码安全的奇幻之旅 date: 2025/05/31 09:34:15 updated: 2025/05/31 09:34:15 author: cmdragon excerpt: 用户认证体系的核心在于用户模型设计和密码安全规范。用户模型需包含唯一用户名、邮箱、加密密码等基础字段,使用SQLAlche…

Kafka ACK机制详解:数据可靠性与性能的权衡之道

在分布式消息系统中&#xff0c;消息确认机制是保障数据可靠性的关键。Apache Kafka 通过 ACK&#xff08;Acknowledgment&#xff09;机制 实现了灵活的数据确认策略&#xff0c;允许用户在 数据可靠性 和 系统性能 之间进行权衡。本文将深入解析 Kafka ACK 机制的工作原理、配…

ARM改口了,小米XRING O1真的是自研芯片

上周小米发布XRING O1芯片的时候&#xff0c;业内议论纷纷。有人说这不过是换个马甲的ARM方案&#xff0c;有人质疑小米的技术实力。但是这两天&#xff0c;ARM官方主动出来澄清了——小米的XRING O1确实没有使用ARM的CSS客户端平台解决方案。 这个转折挺有意思的。ARM作为IP授…

android 媒体框架之MediaCodec

一、MediaCodec 整体架构与设计思想 MediaCodec 是 Android 底层多媒体框架的核心组件&#xff0c;负责高效处理音视频编解码任务。其架构采用 生产者-消费者模型&#xff0c;通过双缓冲区队列&#xff08;输入/输出&#xff09;实现异步数据处理&#xff1a; 输入缓冲区队列…

浅谈 PAM-2 到 PAM-4 的信令技术演变

通信信令技术演进&#xff1a;从 PAM-2 到 PAM-4 在当今数字化高速发展的时代&#xff0c;数据传输需求呈爆炸式增长&#xff0c;行业对通信带宽的要求愈发严苛。为顺应这一趋势&#xff0c;通信信令技术不断革新&#xff0c;曾经占据主导地位的不归零&#xff08;NRZ&#xff…

(3)Playwright自动化-3-离线搭建playwright环境

1.简介 如果是在公司局域网办公&#xff0c;或者公司为了安全对网络管控比较严格这种情况下如何搭建环境&#xff0c;我们简单来看看 &#xff08;第一种情况及解决办法&#xff1a;带要搭建环境的电脑到有网的地方在线安装即可。 &#xff08;第二种情况及解决办法&#xf…

调用蓝耘Maas平台大模型API打造个人AI助理实战

目录 前言需求分析与环境配置明确需求环境准备选择合适的大模型 蓝耘Mass平台介绍API调用大模型API介绍API 调用流程 可交互AI助理开发总结 前言 大数据时代&#xff0c;个人隐私很难得到保障&#xff0c;如果我们需要借助大模型解决一些私人问题&#xff0c;又不想隐私被泄露…

智联未来:低空产业与AI新纪元-(下)

1. 隐形战场&#xff1a;全球规则制定权争夺战 低空经济的崛起&#xff0c;本质是数字主权的争夺战。当美国FAA将无人机适航认证周期延长至36个月&#xff0c;欧盟推出"天空云图"计划整合全境飞行数据时&#xff0c;中国正以制度创新构建自己的规则体系。 1.1 空域…

关于销售的几点注意事项

一、把客户当朋友聊 做买卖这事儿啊&#xff0c;说白了就是人和人打交道。您要是见着客户就背产品说明书&#xff0c;人家扭头就走。得学会听对方说话&#xff0c;琢磨他到底想要啥。就像您去菜市场买菜&#xff0c;摊主要是光说"这菜新鲜"&#xff0c;您可能没感觉…