完整代码在最后~ 这个js作为脚本导入html就可以直接使用~
一、展开/隐藏子级
- 查找所有子级元素
- 隐藏所有子级元素
- 给父级元素添加点击事件(展开/关闭)
hook.doneEach(function () {
// 1. 查询所有子菜单
var subMenus = document.querySelectorAll('.sidebar-nav li > ul');//所有直接嵌套在 <li> 元素内部的 <ul> 元素
subMenus.forEach(function (ul) {
var parent = ul.previousElementSibling; // 每个子级元素对应的 父级元素
// 2. 隐藏子菜单
ul.style.display = 'none';
// 3. 设置父级元素的点击事件
parent.onclick = function (e) {
var isDisplayed = ul.style.display === 'block';
ul.style.display = isDisplayed ? 'none' : 'block';
};
});
});
hook.doneEach
:这个钩子可以让代码内容在 每次页面内容被解析和渲染完成后 调用。
二、点击子页面刷新时 不更改目录显示状态
此时预览页面会发现,点击子页面刷新后,目录的展开状态也被刷新了,所以为了能够保持目录的状态,需要将数据保存再在刷新后重现。
但是我这里多一个要求,即如果重新刷新了整个网页(窗口卸载)的情况下,需要恢复至初始所有隐藏状态。
- 在点击事件中保存当前菜单状态
- 在设置菜单隐藏前添加判断逻辑,是否保存了菜单状态
- 添加一个窗口卸载的监听器,该情况下清除保存数据
/* 注意!这里是代码片段!不是完全连续的!*/
// 2. 从 localStorage 中恢复菜单栏状态
var storedDisplay = localStorage.getItem('sidebar_' + parent.textContent.trim());
if (storedDisplay) {
ul.style.display = storedDisplay;
} else {
ul.style.display = 'none';
}
// 1. 点击事件中添加保存数据操作
parent.onclick = function (e) {
var isDisplayed = ul.style.display === 'block';
ul.style.display = isDisplayed ? 'none' : 'block';
localStorage.setItem('sidebar_' + parent.textContent.trim(), ul.style.display);// 保存到 localStorage
};
// 3. 在窗口卸载前,清除所有保存的状态
window.addEventListener('beforeunload', function () {
var subMenus = document.querySelectorAll('.sidebar-nav li > ul');
subMenus.forEach(function (ul) {
var parent = ul.previousElementSibling;
localStorage.removeItem('sidebar_' + parent.textContent.trim());
});
});
三、在父级目录前添加箭头图标以示状态
- 加载箭头图标,并设置样式
- 添加图标到父级元素前
- 点击事件内添加旋转操作(注意刷新页面的恢复也要恢复图片的旋转状态)
完整代码:
(function (hook, vm) {
window.$docsify = window.$docsify || {};
window.$docsify.plugins = (window.$docsify.plugins || []).concat(function(hook, vm) {
hook.doneEach(function () {
var subMenus = document.querySelectorAll('.sidebar-nav li > ul');
subMenus.forEach(function (ul) {
var parent = ul.previousElementSibling;
// 加载图标
var icon = parent.querySelector('img') || document.createElement('img');
icon.src = './images/UI/arrow.png';
icon.className = 'expand-icon'; // 添加类名
icon.style.width = '25px';
icon.style.height = '25px';
icon.style.marginRight = '8px';
icon.style.verticalAlign = 'middle'; // 设置垂直对齐,使图标与文本水平居中
icon.style.transition = 'transform 0.5s'; // 平滑转动动画
// 将图标添加到标题前
if (!parent.contains(icon)) {
parent.insertBefore(icon, parent.firstChild);
}
// 恢复菜单状态
var storedDisplay = localStorage.getItem('sidebar_' + parent.textContent.trim());
if (storedDisplay) {
ul.style.display = storedDisplay;
icon.style.transform = storedDisplay === 'block' ? 'rotate(90deg)' : 'rotate(0deg)';
} else {
ul.style.display = 'none';
icon.style.transform = 'rotate(0deg)';
}
//开合菜单
parent.onclick = function (e) {
e.stopPropagation(); // 停止事件传播到父元素
var isDisplayed = ul.style.display === 'block';
ul.style.display = isDisplayed ? 'none' : 'block';
icon.style.transform = isDisplayed ? 'rotate(0deg)' : 'rotate(90deg)';
// 保存当前状态到本地存储
localStorage.setItem('sidebar_' + parent.textContent.trim(), ul.style.display);
};
});
});
// 窗口卸载时刷新,清除存储
window.addEventListener('beforeunload', function () {
var subMenus = document.querySelectorAll('.sidebar-nav li > ul');
subMenus.forEach(function (ul) {
var parent = ul.previousElementSibling;
localStorage.removeItem('sidebar_' + parent.textContent.trim());
});
});
});
})();