最近在用vue写官网,别问我为什么用vue写官网,问就是不会jq。。。。vue都出现11年了。。。
左侧目录:点击时,右侧区域可以自动滚动到指定的位置。
右侧区域手动滚动时,左侧锚点可以自动切换到对应的目录上
从而实现双向锚点的功能:
直接上代码:
解决步骤1:左侧锚点部分
<div class="leftFixed"><div @click="goId('shouyeId')" :class="[currentId == 'shouyeId' ? 'activeCls' : '']">我的首页</div><div @click="goId('gongchangId')" :class="[currentId == 'gongchangId' ? 'activeCls' : '']">工厂展示&档案</div><div @click="goId('chanxianId')" :class="[currentId == 'chanxianId' ? 'activeCls' : '']">产线展示</div><div @click="goId('gongyiId')" :class="[currentId == 'gongyiId' ? 'activeCls' : '']">工艺能力&制程</div><div @click="goId('chanpinId')" :class="[currentId == 'chanpinId' ? 'activeCls' : '']">产品展示</div><div @click="goId('tixiId')" :class="[currentId == 'tixiId' ? 'activeCls' : '']">体系认证</div><div @click="goId('pingjiaId')" :class="[currentId == 'pingjiaId' ? 'activeCls' : '']">用户评价</div></div>
对应的css代码:
<style lang="less" scoped>
.leftFixed {position: fixed;top: 100px;left: 110px;background: #fff;padding: 16px;display: flex;flex-direction: column;div {padding: 5px 0;padding-left: 20px;cursor: pointer;&.activeCls {color: #1f833b;font-weight: bold;position: relative;&:before {content: "";/* 使用空内容 */position: absolute;top: 50%;transform: translateY(-50%);left: 0;width: 0;height: 0;border-top: 5px solid transparent;/* 控制箭头的上半部分 */border-bottom: 5px solid transparent;/* 控制箭头的下半部分 */border-left: 10px solid #1f833b;/* 控制箭头的右侧,创建箭头效果 */}}}
}
</style>
对应的js代码:
goId(id) {document.getElementById(id).scrollIntoView();this.currentId = id;//用于显示当前指定的目录
},
解决步骤2:右侧区域滚动后左侧自动回显
在mounted
方法中写以下代码:
if(document.getElementById("content_wrap")){window.addEventListener("scroll",() => {let elements = document.getElementById("content_wrap").querySelectorAll("[id]"); // 获取所有有ID的元素let scrollPosition = window.pageYOffset || document.documentElement.scrollTop;let closestElement = null;let closestDistance = Infinity;elements.forEach((element) => {let distance = Math.abs(element.getBoundingClientRect().top - scrollPosition);if (distance < closestDistance) {closestDistance = distance;closestElement = element;}});if (closestElement && closestElement.id && typeof closestElement.id == "string") {this.currentId = closestElement.id;console.log("Closest element to the top of the viewport is:", closestElement.id);}},{ passive: true, capture: true }
); // 使用捕获阶段和标记为 passive 以提高性能
}
注意一定要写{ passive: true, capture: true }
,否则可能会出现监听不到scroll
的情况
通过上面的两个步骤,就可以实现双向锚点的效果了!!!
多多积累,多多收获!!!
当我把上面的代码发给cursor
帮我进行解析时,它给我了可以添加【防抖/节流】的优化建议:
让我帮你分析这段代码。这是一个用于监听滚动事件并确定最接近视口顶部元素的代码段。让我逐步分析:
1.初始条件判断:
if(document.getElementById(“content_wrap”))
首先检查页面中是否存在 ID 为 content_wrap 的元素,这是一个安全检查。
2.事件监听器设置:
window.addEventListener(“scroll”, callback, { passive: true, capture: true })
在窗口上添加滚动事件监听器
passive: true - 告诉浏览器这个监听器不会调用 preventDefault(),这可以提高滚动性能
capture: true - 使用事件捕获而不是冒泡,确保这个监听器最先执行
3.元素获取:
let elements = document.getElementById(“content_wrap”).querySelectorAll(“[id]”)
在 content_wrap 容器内查找所有带有 ID 属性的元素
使用属性选择器 [id] 来选择这些元素
4.滚动位置获取:
let scrollPosition = window.pageYOffset || document.documentElement.scrollTop
获取当前页面的垂直滚动位置
使用降级方案确保兼容性
5.查找最近元素的逻辑:
let closestElement = null;
let closestDistance = Infinity;
elements.forEach((element) => {
let distance = Math.abs(element.getBoundingClientRect().top - scrollPosition);
if (distance < closestDistance) {
closestDistance = distance;
closestElement = element;
}
});
初始化变量来跟踪最近的元素
遍历所有带 ID 的元素
计算每个元素到视口顶部的距离
更新距离最近的元素
6.结果处理:
if (closestElement && closestElement.id && typeof closestElement.id == “string”) {
this.currentId = closestElement.id;
console.log(“Closest element to the top of the viewport is:”, closestElement.id);
}
进行安全检查确保找到了元素且有有效的 ID
更新 currentId 属性
在控制台输出最近元素的 ID
主要用途:
这段代码通常用于实现滚动监听功能,比如文档导航或目录高亮
可以用来实现类似于"目录跟随阅读位置"的功能
对于长文档或教程类网站特别有用
性能考虑:
使用了 passive: true 提高滚动性能
但是每次滚动都会触发 DOM 查询和计算,如果元素很多可能会影响性能
可以考虑添加节流(throttle)来优化性能
改进建议:
可以添加防抖或节流来减少计算频率
考虑缓存 elements 查询结果,避免每次滚动都查询 DOM
可以添加一个最小距离阈值,只在距离大于某个值时才更新
考虑使用 Intersection Observer API 作为替代方案,可能更高效
根据上面提供的改进建议,我让cursor
帮我实现了一下防抖/节流的效果
我来帮你添加防抖函数来优化这段代码。下面是添加防抖后的代码:
// 首先定义一个防抖函数
function debounce(func, wait) {let timeout;return function executedFunction(...args) {const later = () => {clearTimeout(timeout);func(...args);};clearTimeout(timeout);timeout = setTimeout(later, wait);};
}// 使用防抖优化后的滚动监听代码
if(document.getElementById("content_wrap")){// 将滚动处理函数单独提取出来const handleScroll = () => {let elements = document.getElementById("content_wrap").querySelectorAll("[id]");let scrollPosition = window.pageYOffset || document.documentElement.scrollTop;let closestElement = null;let closestDistance = Infinity;elements.forEach((element) => {let distance = Math.abs(element.getBoundingClientRect().top - scrollPosition);if (distance < closestDistance) {closestDistance = distance;closestElement = element;}});if (closestElement && closestElement.id && typeof closestElement.id == "string") {this.currentId = closestElement.id;console.log("Closest element to the top of the viewport is:", closestElement.id);}};// 创建防抖后的处理函数,设置 200ms 的延迟const debouncedHandleScroll = debounce(handleScroll, 200);// 添加事件监听器,使用防抖后的函数window.addEventListener("scroll",debouncedHandleScroll,{ passive: true, capture: true });
}
这个优化版本的主要改进点:
添加防抖函数:
通过 debounce 函数实现防抖
防抖函数会等待用户停止滚动 200ms 后才执行计算
这样可以显著减少函数执行次数
函数结构优化:
将滚动处理逻辑提取为单独的 handleScroll 函数
使用 debounce 包装处理函数
保持了原有的功能,但大大提升了性能
防抖时间设置:
设置了 200ms 的防抖延迟时间
这个时间可以根据实际需求调整
时间设置太短可能效果不明显,太长可能影响用户体验
上面的代码还可以二次优化,因为这个页面全是静态数据,也就是可以通过下面的方法来处理
额外的优化建议:
1.缓存 DOM 查询:
if(document.getElementById("content_wrap")){// 缓存 content_wrap 元素const contentWrap = document.getElementById("content_wrap");// 缓存所有带 ID 的元素(如果元素列表不会动态变化)const elements = contentWrap.querySelectorAll("[id]");const handleScroll = () => {let scrollPosition = window.pageYOffset || document.documentElement.scrollTop;let closestElement = null;let closestDistance = Infinity;elements.forEach((element) => {// ... 其余代码不变});};const debouncedHandleScroll = debounce(handleScroll, 200);window.addEventListener("scroll", debouncedHandleScroll, { passive: true, capture: true });
}
2.添加清理函数
if(document.getElementById("content_wrap")){// ... 其他代码// 在组件卸载时清理事件监听const cleanup = () => {window.removeEventListener("scroll", debouncedHandleScroll);};// 如果在 Vue 或 React 组件中使用,可以在组件卸载时调用 cleanupreturn cleanup; // 返回清理函数
}
这些优化可以显著提高代码的性能,特别是在长页面或频繁滚动的情况下。防抖可以有效减少不必要的计算,同时保持功能的响应性。根据具体使用场景,你可以调整防抖的时间间隔来平衡性能和用户体验。