之前在社区发布一个关于折叠大纲的代码求助(希望鼠标悬浮自动展开大纲 - 链滴),很高兴看到有热心的人进行帮助,但是最近复习的时候,发现有一点需要优化一下
大纲折叠可以方便我们快速获取文章的大致结构,方便我们去进行定位,但是当我们复习的时候,也就是鼠标点击大纲跳到对应的标题时,如果想回去大纲再进行点击,就需要再次寻找标题对应的大纲,然而此时大纲的加深效果(光标对应标题的大纲定位效果)已经消失,寻找对应的标题就需要重新一个字一个字的核对,这对复习效果造成极大的不便,望请大佬们帮我修改下代码片段
这是目前的效果
这是我理想的效果
示例代码
// see https://ld246.com/article/1727096963532
(async ()=>{
whenElementExist('.sy__outline > .fn__flex-1').then(async el => {
// 监听大纲标题被添加
observeChildAddition(el, node => {
return node.tagName.toLowerCase() === 'ul' &&
node.classList.contains('b3-list') &&
node.querySelector('.b3-list-item')
}, uls => {
// 获取大纲列表
const ul = uls[0];
// 遍历大纲第一级子元素
Array.from(ul.children).forEach(item => {
// 初始时,仅打开第一级
if(item.tagName === 'LI') {
const toggleBtn = item.querySelector('.b3-list-item__toggle');
const svg = toggleBtn?.querySelector('svg.b3-list-item__arrow');
if(!svg.classList.contains('b3-list-item__arrow--open')) {
svg.classList.add('b3-list-item__arrow--open');
}
}
if(item.tagName === 'UL') {
if(item.classList.contains('fn__none')) {
item.remove('fn__none');
}
// 初始时,隐藏第一级下面的后代元素
itemsShow(item, false);
}
// 监听大纲鼠标移入事件
const ul = item.tagName === 'LI' ? item.nextElementSibling : item;
item.addEventListener('mouseenter', (event) => {
if(!ul || ul?.tagName !== 'UL') return;
// 鼠标移入显示第一级后面的后代元素
itemsShow(ul, true);
})
// 监听大纲鼠标移出事件
item.addEventListener('mouseleave', (event) => {
if(!ul || ul?.tagName !== 'UL') return;
// 鼠标移出隐藏第一级后面的后代元素
itemsShow(ul, false);
});
});
});
});
// 动态显示隐藏子标题
function itemsShow(ul, isOpen) {
if(isOpen){
const svgs = ul.querySelectorAll('span.b3-list-item__toggle svg:not(.b3-list-item__arrow--open)');
svgs.forEach(item => {
item.classList.add('b3-list-item__arrow--open');
});
const uls = ul.querySelectorAll('ul.fn__none');
uls.forEach(item => {
item.classList.remove('fn__none');
});
} else {
const svgs = ul.querySelectorAll('span.b3-list-item__toggle svg.b3-list-item__arrow--open');
svgs.forEach(item => {
item.classList.remove('b3-list-item__arrow--open');
});
const uls = ul.querySelectorAll('ul:not(.fn__none)');
uls.forEach(item => {
item.classList.add('fn__none');
});
}
}
function observeChildAddition(el, filter, handler) {
// 配置观察器选项
const config = { attributes: false, childList: true, subtree: false };
// 定义回调函数
const callback = function(mutationsList, observer) {
// 遍历 mutation 列表
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log(mutation, 111);
// 查找新增加的具有类名 'b3-list' 的 ul 元素
const newULs = Array.from(mutation.addedNodes).filter(node => node.nodeType === Node.ELEMENT_NODE && filter(node));
// 如果有新的 ul 元素被添加,则调用处理函数
if(newULs.length > 0) {
handler(newULs);
}
}
}
};
// 创建一个新的 MutationObserver 实例
const observer = new MutationObserver(callback);
// 开始观察目标节点
observer.observe(el, config);
// 返回一个函数来停止观察
return () => {
observer.disconnect();
};
}
// 等待元素渲染完成后执行
function whenElementExist(selector, bySetTimeout = false, delay = 40) {
return new Promise(resolve => {
const checkForElement = () => {
let element = null;
if (typeof selector === 'function') {
element = selector();
} else {
element = document.querySelector(selector);
}
if (element) {
resolve(element);
} else {
if (bySetTimeout) {
setTimeout(checkForElement, delay);
} else {
requestAnimationFrame(checkForElement);
}
}
};
checkForElement();
});
}
})();