前言
重新设计编写一个左侧渲染二级菜单,右侧展示内容的demo项目,内容页包含展示table表格页面,echarts可视化图表页面,后续可能会继续添加页面。
文件构造,如下图
整体预览图,如下图:
一、主体页面
index.html 页面基础代码
<!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>多级菜单+内容页</title>
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/style.css" />
<script src="img/icon/iconfont.js"></script>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<!-- 主体 -->
<div class="content">
<!-- 菜单 -->
<div class="content-item menu-section" id="menuSection"></div>
<!-- 页面 -->
<div class="content-item page-section">
<!-- 头部标题部分-->
<header>
<h3>秘密小屋</h3>
</header>
<div class="iframe-section">
<iframe name="mainFrame" id="mainFrame" frameborder="1" src=""></iframe>
</div>
</div>
</div>
<script src="js/index.js"></script>
</body>
</html>
二、菜单栏及内容页
多级菜单,并且默认展开第一个一级菜单的效果,以及右侧展对应内容页。
渲染的 menuData 菜单数组如下,
var menuData = [
{
id: "menu1",
title: "数据可视化图表",
icon: "echart",
children: [
{ id: "menu1-1", title: "柱状图", url: "html/echarts/bar.html" },
{ id: "menu1-2", title: "饼图", url: "html/echarts/pie.html" },
],
},
{
id: "menu2",
title: "表单",
icon: "table",
url: "html/table.html",
children: [
{ id: "menu2-1", title: "柱状图", url: "html/echarts/bar.html" },
{ id: "menu2-2", title: "饼图", url: "html/echarts/pie.html" },
],
},
{
id: "menu3",
title: "关于我们",
icon: "about",
url: "html/about.html",
},
];
采用原生js来实现渲染效果,svg来渲染图标样式,其index.js文件如下
/**
* 多级菜单实现
*
* 菜单数据:menuData
*/
var menuData = [
{
id: "menu1",
title: "数据可视化图表",
icon: "echart",
children: [
{ id: "menu1-1", title: "折线图", url: "html/echarts/line.html" },
{ id: "menu1-2", title: "饼图", url: "html/echarts/pie.html" },
{ id: "menu1-1", title: "柱状图", url: "html/echarts/bar.html" },
],
},
{
id: "menu2",
title: "表单",
icon: "table",
children: [
{ id: "menu2-1", title: "非原生table", url: "html/table/notable.html" },
{ id: "menu2-2", title: "原生table", url: "html/table/Table.html" },
],
},
{
id: "menu3",
title: "关于我们",
icon: "about",
url: "html/about.html",
},
];
// 获取渲染菜单和内容区域元素
var menuSection = document.getElementById("menuSection");
var mainFrame = document.getElementById("mainFrame");
// 使用svg图标,图标更丰富
function createIcon(fatherElement, iconSrc) {
const iconElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
iconElement.setAttribute("class", "icon");
iconElement.setAttribute("aria-hidden", "true");
const useElement = document.createElementNS("http://www.w3.org/2000/svg", "use");
useElement.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "#icon-" + iconSrc);
iconElement.appendChild(useElement);
fatherElement.appendChild(iconElement);
}
function createMenuItem(menuItem, isExpanded) {
const liElement = document.createElement("li");
// 判断是否有图标属性,如果有则添加图标
if (menuItem.icon) {
createIcon(liElement, menuItem.icon);
}
const titleElement = document.createElement("span");
titleElement.textContent = menuItem.title;
titleElement.classList.add("title");
// 添加 click 事件处理函数
liElement.addEventListener("click", function (event) {
event.stopPropagation();
// 如果有子菜单,则展开/折叠子菜单
if (menuItem.children) {
// 首先折叠其他已展开的菜单项
for (let i = 0; i < menuData.length; i++) {
const otherMenuItem = menuData[i];
if (otherMenuItem !== menuItem && otherMenuItem.expanded) {
otherMenuItem.expanded = false;
const otherLiElement = menuSection.querySelector(`li[data-id="${otherMenuItem.id}"]`);
const otherUlElement = otherLiElement.querySelector(".submenu");
otherUlElement.style.display = "none";
}
}
// 切换当前菜单项的展开状态
menuItem.expanded = !menuItem.expanded;
// 根据展开状态设置子菜单的 display 样式
const ulElement = liElement.querySelector(".submenu");
ulElement.style.display = menuItem.expanded ? "block" : "none";
} else {
// 如果点击的是没有子菜单的菜单项,则加载对应的URL
const url = menuItem.url;
if (url) {
mainFrame.src = url;
}
// 切换选中状态
const selectedItems = menuSection.getElementsByClassName("selected");
for (let i = 0; i < selectedItems.length; i++) {
selectedItems[i].classList.remove("selected");
}
liElement.classList.add("selected");
}
});
liElement.appendChild(titleElement);
if (menuItem.children) {
const ulElement = document.createElement("ul");
ulElement.classList.add("submenu");
// 根据展开状态设置子菜单的 display 样式
ulElement.style.display = isExpanded ? "block" : "none";
for (let i = 0; i < menuItem.children.length; i++) {
const childMenuItem = menuItem.children[i];
const childLiElement = createMenuItem(childMenuItem);
childLiElement.setAttribute("data-id", childMenuItem.id);
ulElement.appendChild(childLiElement);
}
liElement.appendChild(ulElement);
} else {
liElement.setAttribute("data-url", menuItem.url);
}
return liElement;
}
// 渲染菜单
function renderMenu(data, parentElement) {
var ulElement = document.createElement("ul");
for (var i = 0; i < data.length; i++) {
var menuItem = data[i];
const isExpanded = i === 0; // 默认展开第一个菜单项
var liElement = createMenuItem(menuItem, isExpanded);
// 如果该菜单项有子菜单,则添加 class="hasChildren" 样式类
if (menuItem.children) {
liElement.classList.add("hasChildren");
}
ulElement.appendChild(liElement);
}
parentElement.appendChild(ulElement);
}
// 初始化菜单
function initMenu() {
renderMenu(menuData, menuSection);
// 默认展示第一个包含 url 的子菜单项并选中
var firstMenuItem = menuData[0];
var foundFirstUrl = false; // 标志位,表示是否找到第一个包含URL的子菜单项
if (firstMenuItem && firstMenuItem.children) {
// 递归函数,用于处理多级菜单情况
function findFirstUrl(menuItems) {
for (var i = 0; i < menuItems.length; i++) {
var menuItem = menuItems[i];
if (menuItem.url) {
mainFrame.src = menuItem.url;
var liElement = menuSection.querySelector('li[data-url="' + menuItem.url + '"]');
liElement.classList.add("selected");
foundFirstUrl = true;
break;
}
if (menuItem.children) {
findFirstUrl(menuItem.children);
if (foundFirstUrl) {
break;
}
}
}
}
findFirstUrl(firstMenuItem.children);
}
// 如果没有找到第一个包含URL的子菜单项,则直接渲染URL
if (!foundFirstUrl && firstMenuItem.url) {
mainFrame.src = firstMenuItem.url;
var liElement = menuSection.querySelector('li[data-url="' + firstMenuItem.url + '"]');
liElement.classList.add("selected");
}
// 点击菜单项展开或收起子菜单
menuSection.addEventListener("click", function (event) {
var target = event.target;
var liElement = target.closest("li");
if (liElement) {
// 是否包含子菜单
var hasChildren = liElement.classList.contains("hasChildren");
// 获取已展开的菜单项
var expandedItems = menuSection.querySelectorAll(".expanded");
// 判断点击的菜单项是否已展开
var isExpanded = liElement.classList.contains("expanded");
// 如果菜单项包含子菜单,则阻止默认行为
if (hasChildren) {
event.preventDefault();
}
// 遍历已展开的菜单项,折叠除当前点击的菜单项以外的其他已展开菜单项
expandedItems.forEach(function (item) {
if (item !== liElement && !liElement.contains(item)) {
item.classList.remove("expanded");
var submenu = item.querySelector(".submenu");
submenu.style.display = "none";
}
});
// 如果点击的菜单项是已展开的,则折叠;否则展开
if (isExpanded) {
liElement.classList.remove("expanded");
if (hasChildren) {
var submenu = liElement.querySelector(".submenu");
submenu.style.display = "none";
}
} else {
liElement.classList.add("expanded");
if (hasChildren) {
var submenu = liElement.querySelector(".submenu");
submenu.style.display = "block";
var firstLink = submenu.querySelector("a");
if (firstLink) {
firstLink.focus();
}
}
}
}
});
}
initMenu();
三、echarts数据可视乎化
可移步至 echarts官网学习
随便添加几个图表,效果如下图,
line.html
<!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>echarts</title>
<link rel="stylesheet" href="../../css/reset.css" />
<link rel="stylesheet" href="../../css/style.css" />
<script src="../../js/echarts.min.js"></script>
</head>
<body>
<div class="echarts-container">
<div class="panel panel01">
<div class="chart"></div>
</div>
</div>
<script src="../../js/initLine.js"></script>
</body>
</html>
initLine.js,
// 折线图
(function () {
// 1实例化对象
var myChart = echarts.init(document.querySelector(".panel01 .chart"));
var yearData = [
{
year: "2092", // 年份
data: [
// 两个数组是因为有两条线
[40, 70, 90, 200, 270, 80, 200, 250, 170, 320],
[120, 220, 120, 150, 100, 280, 100, 210, 320, 200],
[300, 320, 280, 370, 400, 400, 300, 400, 280, 450],
[430, 480, 400, 590, 520, 580, 400, 430, 580, 500],
],
},
];
option = {
// 通过这个color修改两条线的颜色
color: ["#ab285f", "#16406d", "#895d11", "#11c2af"],
tooltip: {
trigger: "axis",
},
legend: {
// 如果series 对象有name 值,则 legend可以不用写data
// 修改图例组件 文字颜色
textStyle: {
color: "#527fb5",
},
// 这个10% 必须加引号
right: "10%",
},
grid: {
top: "20%",
left: "3%",
right: "4%",
bottom: "3%",
show: true, // 显示边框
borderColor: "#012f4a", // 边框颜色
containLabel: true, // 包含刻度文字在内
},
xAxis: {
type: "category",
boundaryGap: false,
data: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月"],
// 去除刻度线
axisTick: {
show: false,
},
// 修改x轴上的文字
axisLabel: {
color: "#4c9bfd",
},
// 去除x轴
axisLine: {
show: false,
},
},
yAxis: {
name: "当日加油/加气量",
type: "value",
// 去除刻度线
axisTick: {
show: false,
},
// 修改y轴上的文字
axisLabel: {
fontSize: 12,
color: "#527fb5",
},
// 去除y轴
axisLine: {
show: false,
},
// y轴分割线的颜色
splitLine: {
lineStyle: {
color: "#012f4a",
},
},
},
series: [
{
name: "92#",
type: "line",
// true 可以让我们的折线显示带有弧度
smooth: true,
data: yearData[0].data[0],
},
{
name: "95#",
type: "line",
smooth: true,
data: yearData[0].data[1],
},
{
name: "0#",
type: "line",
smooth: true,
data: yearData[0].data[2],
},
{
name: "天然气",
type: "line",
smooth: true,
data: yearData[0].data[3],
},
],
};
myChart.setOption(option);
// 4. 让图表跟随屏幕自动的去适应
window.addEventListener("resize", function () {
myChart.resize();
l;
});
})();
四,table表格
预期实现表格样式如下图,
table.html
<!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>table表格</title>
<link rel="stylesheet" href="../../css/reset.css" />
<link rel="stylesheet" href="../../css/style.css" />
</head>
<body>
<div class="table-container">
<div class="panel">
<div class="title">
<p>原生table</p>
</div>
<div class="item-content">
<div id="table"></div>
</div>
</div>
</div>
<script src="../../js/initTable.js"></script>
</body>
</html>
渲染表格 initTable.js
var data = [
{ id: 1001, name: "张三1", gender: "男", age: 20 },
{ id: 1002, name: "李四1", gender: "女", age: 11 },
{ id: 1003, name: "张三2", gender: "男", age: 16 },
{ id: 1004, name: "李四2", gender: "女", age: 18 },
{ id: 1005, name: "张三3", gender: "男", age: 14 },
{ id: 1006, name: "李四3", gender: "女", age: 18 },
{ id: 1007, name: "张三4", gender: "男", age: 21 },
{ id: 1008, name: "李四5", gender: "女", age: 18 },
];
var tableHtml = "<table>";
// 添加表头
tableHtml += "<thead><tr>";
tableHtml += "<th>序号</th>";
tableHtml += "<th>姓名</th>";
tableHtml += "<th>性别</th>";
tableHtml += "<th>年龄</th>";
tableHtml += "</tr></thead>";
// 添加表格数据
tableHtml += "<tbody>";
for (var i = 0; i < data.length; i++) {
tableHtml += "<tr>";
tableHtml += "<td>" + data[i].id + "</td>";
tableHtml += "<td>" + data[i].name + "</td>";
tableHtml += "<td>" + data[i].gender + "</td>";
tableHtml += "<td>" + data[i].age + "</td>";
tableHtml += "</tr>";
}
tableHtml += "</tbody></table>";
document.getElementById("table").innerHTML = tableHtml;
五、关于我们
暂时还没想好写点什么,尽请期待吧
一个跳动的尽请期待,about.html
<!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>关于我们</title>
<link rel="stylesheet" href="../css/reset.css" />
<link rel="stylesheet" href="../css/style.css" />
</head>
<body>
<div class="about-container">
<div class="note" id="note">尽 请 期 待...</div>
</div>
<script>
var noteElement = document.getElementById("note");
setInterval(function () {
// 改变颜色
var red = Math.floor(Math.random() * 256);
var green = Math.floor(Math.random() * 256);
var blue = Math.floor(Math.random() * 256);
noteElement.style.color = "rgb(" + red + "," + green + "," + blue + ")";
}, 500); // 这里的 500 表示每隔 0.5 秒改变一次颜色
</script>
</body>
</html>
最后
就这么多吧,之后想起来什么再继续写点吗,嘿嘿嘿,附上了打包文件,快来下载学习吧
附上style.css
body,
html {
width: 100%;
height: 100vh;
}
.content {
width: 100%;
height: 100%;
box-sizing: border-box;
display: flex;
align-items: flex-start;
justify-content: flex-start;
}
.content .content-item {
height: 100%;
}
iframe {
width: 100%;
height: 100%;
border: none;
}
/* 菜单样式开始 */
.content .menu-section {
width: 15%;
background-color: rgb(40, 40, 40);
}
ul,
li {
list-style: none;
}
li {
text-align: left;
padding: 8px 0px 8px 10px;
color: #bdbdbd;
cursor: pointer;
}
.icon {
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
fill: currentColor;
overflow: hidden;
}
.title {
padding: 0 0 0 10px;
}
.closed {
transform: rotate(-90deg);
transition: all 0.3s ease;
}
.has-children {
position: relative;
}
.open::before {
content: "-";
position: absolute;
left: -1em;
}
.selected {
color: #5da8e3;
background-color: rgba(105, 105, 105, 0.8);
}
.first-level-selected {
background-color: rgba(105, 105, 105, 0.8);
color: #5da8e3;
}
.submenu {
padding: 8px 0px 8px 10px;
}
/* 菜单样式结束 */
/* 页面内容开始 */
.content .page-section {
width: 85%;
}
.content .page-section header {
height: 60px;
text-align: left;
padding: 0 0 0 60px;
background-color: #fff;
/* background-color: rgb(230, 230, 230, 0.8); */
}
.content .page-section header h3 {
line-height: 60px;
font-size: 20px;
color: rgb(97, 97, 97);
}
.content .page-section .iframe-section {
height: calc(100% - 60px);
background-color: rgba(241, 241, 241, 0.8);
overflow: hidden;
}
/* 页面内容结束 */
/* echarts图表样式开始 */
.echarts-container {
width: 100%;
height: 100%;
overflow: hidden;
}
.panel {
width: 100%;
height: 100%;
}
.panel .chart {
width: 100%;
height: 100%;
}
/* echarts图表样式结束 */
/* table表格样式开始 */
.table-container {
width: 100%;
height: 100%;
overflow: hidden;
}
.table-container .panel {
width: calc(100% - 100px);
margin: 0 auto;
}
.table-container .panel .title {
padding: 20px 0;
text-align: center;
font-size: 20px;
color: #1a2748;
}
.table-container .panel .item-content {
width: 100%;
height: 100%;
}
.table-head,
.table-body {
display: flex;
align-items: baseline;
}
.table-head {
background-color: #1a2748;
padding: 1rem 0;
}
.table-head .head-th {
flex: 1;
text-align: center;
color: #fff;
font-size: 16px;
}
.table-body,
.body-td {
flex: 1;
text-align: center;
color: #000;
font-size: 14px;
padding: 5px 0;
}
.table-body:nth-child(2n) {
background-color: #0011b342;
}
table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
background-color: #f9f9f9;
font-size: 14px;
margin-top: 20px;
}
th,
td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #4caf50;
color: white;
}
/* table表格样式结束 */
/* 关于我们样式开始 */
.about-container {
width: 100%;
height: 100%;
overflow: hidden;
}
.note {
line-height: 300px;
text-align: center;
font-size: 100px;
}
.note {
animation: jump 0.5s alternate infinite;
}
@keyframes jump {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-20px);
}
}
/* 关于我们样式结束 */