个人网站备案麻烦,海口旅游类网站建设,连云港网站建设价位,网站物理结构引言
在网页开发中#xff0c;树形菜单是一种非常实用的#xff0c;它可以清晰地展示具有层级关系的数据#xff0c;并且能够方便用户进行导航和操作。 整体思路
整个项目主要分为三个部分#xff1a;HTML 结构搭建、CSS 样式设计和 JavaScript 交互逻辑实现。通过 XMLHt…引言
在网页开发中树形菜单是一种非常实用的它可以清晰地展示具有层级关系的数据并且能够方便用户进行导航和操作。 整体思路
整个项目主要分为三个部分HTML 结构搭建、CSS 样式设计和 JavaScript 交互逻辑实现。通过 XMLHttpRequest 对象从 JSON 文件中获取数据将数据转换为树形结构然后将树形结构渲染为 HTML 元素并添加点击事件处理逻辑实现菜单的展开 / 收起和内容的切换。 效果展示
点击不同的节点会有不同的页面显示并且菜单是在页面的左侧引入的iframe是在页面的右侧。 代码总览
!DOCTYPE html
html langzh-CNheadmeta charsetutf-8 /meta nameviewport contentwidthdevice-width, initial-scale1title树形结构示例/titlestylebody {display: flex;margin: 0;padding: 0;}.left {width: 220px;background-color: #98D5FF;display: flex;flex-direction: column;overflow-y: auto;/* height: 100vh; */}.content {display: flex;align-items: center;padding: 10px;height: 35px;cursor: pointer;}.content img {margin-right: 8px;}.right {background-color: seagreen;flex-grow: 1;height: 100vh;}.box {display: none;margin-left: 20px;}.imgBot {margin-left: 100px;}.imgLeft {margin-right: 10px;}iframe {width: 100%;height: 100%;}/style/headbodyscriptlet data;let xhr new XMLHttpRequest();xhr.open(get, ./js/trees.json, true);xhr.send();//回调函数放一些比较依赖data的代码确保数据请求成功后在进行数据渲染xhr.onreadystatechange function() {if (xhr.readyState 4 xhr.status 200) {let text xhr.responseText;data JSON.parse(text);// 开始构建调用parseTree函数,pid为0开始构建树结构,并将结果存到treeData里let treeData parseTree(data, 0);// 渲染树形结构并插入到body中相当于重新写了一下bodydocument.body.innerHTML renderTree(treeData);// 创建右侧 iframe插入的页面//1.创建一个divlet iframeDiv document.createElement(div);//2.给这个div起名iframeDiv.className right;// 3.把iframe插入到这个div中后续 通过点击的树形节点更新srciframeDiv.innerHTML iframe idcontentFrame src frameborder0/iframe;//4.把创建好的div插入到bodydocument.body.appendChild(iframeDiv);}};//递归构建树形结构的函数function parseTree(data, pid) {// 用来存储树状的数据let tree [];for (let i in data) {//如果数据的 pid 属性等于传入的 pid(父级的id)//⭐子集的pid和父级的id是保持一致的这样才能完成递归if (data[i].pid pid) {tree.push(data[i]);//递归调用parseTree传data和当前数据的id作为pid上面函数里是data和pid为了查找当前数据的子集然后用child子集方法插入到当前数据后。tree[tree.length - 1].child parseTree(data, data[i].id);}}// 返回构建的树形结构return tree;}//转化函数HTML结构function renderTree(data) {// 这个div是左边菜单的底板let str div classleft;for (let i in data) {//如果当前数据有子节点的话就为该数据生成一个包含子节点的htmlif (data[i].child.length 0) {str div classtextBoxdiv classcontentimg classimgLeft src./img/圣诞礼物.png width20px /${data[i].name}img classimgBot src./img/向下箭头.png width20px height20px//divdiv classbox styledisplay:none;;//递归调用renderTree函数,生成当前节点的子节点只有在有子节点的条件下才递归因为要去检查子节点有没有子节点没有的话就结束str renderTree(data[i].child);//拼结束标签str /div/div;//如果当前数据没有子节点的话就添加普通元素} else {str div classtextBoxdiv classcontentimg classimgLeft src./img/圣诞礼物.png width20px /${data[i].name}/div/div;}}str /div;return str;}// 监听点击事件给body上切换子节点显示/隐藏并更新 iframe 的 srcdocument.body.addEventListener(click, function(e) {//获取点击的目标元素全局的let target e.target;//向上查找父元素找到有content元素while (target !target.classList.contains(content)) {target target.parentElement;}//如果找到了if (target) {// 通过 target.nextElementSibling 获取 target 元素的下一个兄弟元素即紧接着 .content 元素之后的元素。let sibling target.nextElementSibling;// 如果该兄弟元素有box元素if (sibling sibling.classList.contains(box)) {// 展开或收起子节点sibling.style.display sibling.style.display none ? block : none;}// 点击叶子节点更新 iframe//通过target.textContent获取到点击的目标元素文本let name target.textContent.trim();let item null;//1.通过循环在data中找与name相匹配的数据for (let i 0; i data.length; i) {// 2.如果相匹配if (data[i].name name) {// 3.就存到item里item data[i];break;}}// 如果找到匹配的数据且path路径不为空则更新iframe的srcif (item item.path) {document.getElementById(contentFrame).src item.path;}}});/script/body
/html css 部分
body {display: flex;margin: 0;padding: 0;}.left {width: 220px;background-color: #98D5FF;display: flex;flex-direction: column;overflow-y: auto;/* height: 100vh; */}.content {display: flex;align-items: center;padding: 10px;height: 35px;cursor: pointer;}.content img {margin-right: 8px;}.right {background-color: seagreen;flex-grow: 1;height: 100vh;}.box {display: none;margin-left: 20px;}.imgBot {margin-left: 100px;}.imgLeft {margin-right: 10px;}iframe {width: 100%;height: 100%;}
JS部分
1.数据请求
使用 XMLHttpRequest 对象从 ./js/trees.json 文件中获取数据。当请求成功后将数据解析为 JSON 对象并调用 parseTree 函数将数据转换为树形结构。调用 renderTree 函数将树形结构转换为 HTML 字符串并插入到页面的 body 元素中。创建一个包含 iframe 的 div 元素并添加到页面的 body 元素中。 let data;let xhr new XMLHttpRequest();xhr.open(get, ./js/trees.json, true);xhr.send();//回调函数放一些比较依赖data的代码确保数据请求成功后在进行数据渲染xhr.onreadystatechange function() {if (xhr.readyState 4 xhr.status 200) {let text xhr.responseText;data JSON.parse(text);// 开始构建调用parseTree函数,pid为0开始构建树结构,并将结果存到treeData里let treeData parseTree(data, 0);// 渲染树形结构并插入到body中相当于重新写了一下bodydocument.body.innerHTML renderTree(treeData);// 创建右侧 iframe插入的页面//1.创建一个divlet iframeDiv document.createElement(div);//2.给这个div起名iframeDiv.className right;// 3.把iframe插入到这个div中后续 通过点击的树形节点更新srciframeDiv.innerHTML iframe idcontentFrame src frameborder0/iframe;//4.把创建好的div插入到bodydocument.body.appendChild(iframeDiv);
2.构建树形结构的函数
通过递归的方式将数据转换为树形结构每个节点的子节点存储在 child 属性中。
function parseTree(data, pid)定义一个递归函数用于将扁平的数据转换为树形结构。
let tree [];创建一个空数组 tree 用于存储树形结构的数据。
for (let i in data)遍历数据数组。
if (data[i].pid pid)如果当前数据的 pid 属性等于传入的 pid表示该数据是当前节点的子节点。
tree.push(data[i]);将该数据添加到 tree 数组中。
tree[tree.length - 1].child parseTree(data, data[i].id);递归调用 parseTree 函数查找当前数据的子节点并将结果存储在 child 属性中。
function parseTree(data, pid) {// 用来存储树状的数据let tree [];for (let i in data) {//如果数据的 pid 属性等于传入的 pid(父级的id)//⭐子集的pid和父级的id是保持一致的这样才能完成递归if (data[i].pid pid) {tree.push(data[i]);//递归调用parseTree传data和当前数据的id作为pid上面函数里是data和pid为了查找当前数据的子集然后用child子集方法插入到当前数据后。tree[tree.length - 1].child parseTree(data, data[i].id);}}// 返回构建的树形结构return tree;}
3.转化函数
根据树形结构生成 HTML 字符串对于有子节点的菜单项添加展开 / 收起的箭头图标。
function renderTree(data)定义一个函数用于将树形结构转换为 HTML 字符串。
data 是树形结构的数据。
let str div classleft;初始化一个字符串 str用于存储 HTML 代码。
for (let i in data)遍历树形结构的数据。
if (data[i].child.length 0)如果当前元素有子节点则生成包含子节点的 HTML 代码。str ...拼接 HTML 代码包括左侧的图片、节点名称和向下箭头图标。
str renderTree(data[i].child);递归调用 renderTree 函数生成子节点的 HTML 代码。str /div/div;拼接结束标签。
else如果当前元素没有子节点则生成普通的 HTML 代码。
str /div;拼接结束标签。return str;返回生成的 HTML 字符串。
function renderTree(data) {// 这个div是左边菜单的底板let str div classleft;for (let i in data) {//如果当前数据有子节点的话就为该数据生成一个包含子节点的htmlif (data[i].child.length 0) {str div classtextBoxdiv classcontentimg classimgLeft src./img/圣诞礼物.png width20px /${data[i].name}img classimgBot src./img/向下箭头.png width20px height20px//divdiv classbox styledisplay:none;;//递归调用renderTree函数,生成当前节点的子节点只有在有子节点的条件下才递归因为要去检查子节点有没有子节点没有的话就结束str renderTree(data[i].child);//拼结束标签str /div/div;//如果当前数据没有子节点的话就添加普通元素} else {str div classtextBoxdiv classcontentimg classimgLeft src./img/圣诞礼物.png width20px /${data[i].name}/div/div;}}str /div;return str;}
4.点击事件处理函数
监听 body 元素的点击事件当点击菜单项时切换子菜单的显示 / 隐藏状态。当点击叶子节点时根据节点的 path 属性更新 iframe 的 src 属性显示相应的内容。
document.body.addEventListener(click, function(e)为页面的 body 元素添加一个点击事件监听。
let target e.target;获取点击事件的目标元素
while (target !target.classList.contains(content))向上查找父元素直到找到具有 content 类名的元素。
if (target)如果找到了具有 content 类名的元素的话就let sibling target.nextElementSibling;获取目标元素的下一个兄弟元素。
if (sibling sibling.classList.contains(box))如果兄弟元素具有 box 类名说明它是子菜单。
sibling.style.display sibling.style.display none ? block : none;切换子菜单的显示状态实现展开和收起的效果。
let name target.textContent.trim();获取目标元素的文本内容并去除首尾的空格。
let item null;初始化一个变量 item用于存储匹配的元素。
for (let i 0; i data.length; i)遍历 data 数组查找与目标元素文本内容匹配的元素。
if (data[i].name name)如果找到匹配的元素。
item data[i];将匹配的元素存储在 item 变量中。break;跳出循环。
if (item item.path)如果找到匹配的元素且该元素具有 path 属性。document.getElementById(contentFrame).src item.path;将 iframe 的 src 属性设置为该元素的 path 属性值从而在 iframe 中加载对应的页面。
document.body.addEventListener(click, function(e) {//获取点击的目标元素全局的let target e.target;//向上查找父元素找到有content元素while (target !target.classList.contains(content)) {target target.parentElement;}//如果找到了if (target) {// 通过 target.nextElementSibling 获取 target 元素的下一个兄弟元素即紧接着 .content 元素之后的元素。let sibling target.nextElementSibling;// 如果该兄弟元素有box元素if (sibling sibling.classList.contains(box)) {// 展开或收起子节点sibling.style.display sibling.style.display none ? block : none;}// 点击叶子节点更新 iframe//通过target.textContent获取到点击的目标元素文本let name target.textContent.trim();let item null;//1.通过循环在data中找与name相匹配的数据for (let i 0; i data.length; i) {// 2.如果相匹配if (data[i].name name) {// 3.就存到item里item data[i];break;}}// 如果找到匹配的数据且path路径不为空则更新iframe的srcif (item item.path) {document.getElementById(contentFrame).src item.path;}}});