顶部tab点击时跳到指定位置

本文介绍了如何实现顶部tab点击后页面自动滚动到相应内容区域。通过HTML页面代码和JS方法绑定,轻松完成这一功能。只需简单几步,即可让顶部tab具备定位跳转的效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在实现一个顶部tab点击时跳到指定位置的功能,那么我们如何来实现顶部tab点击时跳到指定位置的功能的?下面我们来实现一下

html页面代码:

<div class="tabs" id="gudingweizhi" style="position: absolute; width: 100%;">
                    <div class="backbox"></div>
                    <div class="links Lcfx">
                        <a><h2 class="link active" id="jiudianyudingclick" data-type="order">酒店预订</h2></a>
                        <a><h2 class="link" id="jiaotongweizhiclick" data-type="router">交通位置</h2></a>
                        <a><h2 class="link" id="jiudiankaikuangclick" data-type="intro">酒店概况</h2></a>
                        <a><h2 class="link" id="jiudiandianpingclick" data-type="comment">酒店点评</h2></a>
                    </div>
</div>

JS页面方法绑定:

//酒店点评点击
        $("#jiudiandianpingclick").click(function () {
            $(".Pdetail_part2 h2").removeClass('active');
            $("#jiudiandianpingclick").addClass('active');
            //设置定时器
            timer = setInterval(function () {
                var osTop = document.getElementById('jiudiandianpingdiv').offsetTop - 44;//获取内容距离顶部的高度
                var meicigaodu = window.pageYOffset;//当前高度
                var ispeed; //每次执行滚动的高度
                if (Math.abs(meicigaodu - osTop) > 500) {
                    ispeed = Math.floor(osTop / 2);
                }
                else {
                    ispeed = Math.floor(osTop / 6);
                }
                if (meicigaodu >= osTop) {
                    if ((meicigaodu - osTop) > ispeed) {
                        meicigaodu -= ispeed;
                    }
                    else {
                        meicigaodu = osTop;
                    }
                    document.documentElement.scrollTop = document.body.scrollTop = meicigaodu;//滚动条距离顶部的高度
                    if (osTop >= meicigaodu) {
                        clearInterval(timer);
                    }
                }
                else {
                    if ((ispeed + meicigaodu) < osTop) {
                        meicigaodu += ispeed;
                    }
                    else {
                        meicigaodu = osTop;
                    }
                    document.documentElement.scrollTop = document.body.scrollTop = meicigaodu;//滚动条距离顶部的高度
                    if (osTop <= meicigaodu) {
                        clearInterval(timer);
                    }
                }
            }, 60);
        });

效果截图:
在这里插入图片描述
这样你就可以实现顶部tab点击时跳到指定位置功能了,是不是很简单呢。

<think>我们不再使用isUserClick标志,而是采用另一种思路:在用户点击立即保存索引,并且让滚动事件中的索引更新逻辑不覆盖用户点击的索引。 具体做法: 1. 在用户点击,我们立即将activeIndex设置为用户点击的索引,并开始滚动。 2. 在滚动事件中,我们设置一个标记,表示当前滚动是由点击触发的,并且在一段间内(比如滚动过程中)忽略滚动事件对activeIndex的更新。 3. 但是用户要求不用isClick,所以我们换一种方式:在用户点击,我们设置一个临变量来记录点击的索引,然后在滚动事件中,如果当前滚动位置与用户点击的目标位置相差较大,则仍然使用滚动计算;如果接近目标位置,则忽略滚动计算,保持用户点击的索引。 然而,用户希望:用户点击tab就是选中的tab,而不会被滚动覆盖。所以我们可以这样: 在用户点击,我们设置activeIndex为用户点击的索引,然后滚动。在滚动事件中,我们暂不更新activeIndex,直到滚动结束(或者设置一个定器,在滚动结束后再允许更新)。 但是,用户要求不用isClick,所以我们可以通过比较当前滚动位置是否接近用户点击的目标位置来判断。如果接近,则保持用户点击的索引,否则按照正常滚动计算。 然而,这种方法可能比较复杂。另一种简单的思路是:在用户点击,我们设置一个标志(尽管用户说不用,但为了功能可能需要),但我们不用isClick这个名字,而是用另一种方式。 我们换一个思路:在用户点击,我们记录点击间戳,然后在滚动事件中,如果距离点击间很近(比如500ms内),则忽略滚动事件对索引的更新。这样就不需要isClick变量,而是用间戳判断。 具体步骤: 1. 添加一个ref:const lastClickTime = ref(0); 2. 在scrollToSection中,记录点击间:lastClickTime.value = Date.now(); 3. 在handleScroll中,判断当前间与lastClickTime.value的间差,如果小于某个阈值(比如500ms),则直接返回,不更新activeIndex。 这样,在用户点击后的500ms内,滚动事件不会更新activeIndex,因此用户点击的索引会保持。500ms后,滚动事件可以正常更新索引。 但是,500ms可能不够,因为滚动可能需要更长间。我们可以改为:在滚动过程中(即从点击开始到滚动结束)都忽略滚动事件?但这样又需要知道滚动何结束,而滚动结束事件并不容易捕获。 因此,我们结合间戳和位置判断:在用户点击后的一段间内(比如1000ms),或者当滚动位置接近目标位置,我们才允许更新activeIndex。 然而,用户要求不用isClick,所以我们避免使用布尔标志,而是用间戳和位置判断。 但是,这样逻辑会复杂。考虑到功能正确性,我们使用间戳的方式,并设置一个较长的保护间(如1000ms)可能更简单。 我们采用以下方案: 修改后的代码: ```typescript // 新增状态 const lastClickTime = ref(0); const lastClickIndex = ref(0); const scrollToSection = (index: number) => { // 保存用户点击的索引和间 lastClickIndex.value = index; lastClickTime.value = Date.now(); activeIndex.value = index; // 立即更新 if (sectionRefs.value[index]) { const headerHeight = getSearchHeaderHeight(); const targetOffset = sectionRefs.value[index].offsetTop - headerHeight; sectionContainerRef.value?.scrollTo({ top: targetOffset, behavior: 'smooth' }); } }; const handleScroll = () => { if (!sectionContainerRef.value || sectionRefs.value.length === 0) return; // 如果距离上次点击间小于1000ms,则不更新activeIndex(保持点击的索引) if (Date.now() - lastClickTime.value < 1000) { return; } // 原有的滚动索引计算逻辑 const scrollTop = sectionContainerRef.value.scrollTop; const containerHeight = sectionContainerRef.value.clientHeight; const scrollHeight = sectionContainerRef.value.scrollHeight; const headerHeight = getSearchHeaderHeight(); // 底部检测 if (scrollTop + containerHeight >= scrollHeight - 5) { activeIndex.value = sectionRefs.value.length - 1; return; } // 计算有效滚动位置(考虑搜索表单高度) const effectiveScrollTop = scrollTop + headerHeight; let currentIndex = 0; for (let i = 0; i < sectionRefs.value.length; i++) { if (sectionRefs.value[i].offsetTop <= effectiveScrollTop) { currentIndex = i; } else { break; } } activeIndex.value = currentIndex; }; ``` 这个方案在用户点击后1秒内,滚动事件不会更新activeIndex,因此用户点击的索引会一直保持。1秒后,滚动事件可以正常更新索引。 但是,1秒可能不够(如果滚动动画超过1秒)?或者可能太长(用户滚动操作被延迟1秒响应)?我们可以调整为500ms,并测试滚动动画间。 另外,在滚动过程中,如果用户点击了另一个tab,那么lastClickTime会被更新,所以不会出现冲突。 但是,如果用户快速滚动,然后点击,这个保护机制也能工作。 然而,这个方案有一个缺点:在保护间内,用户手动滚动不会更新索引,直到超过1秒。但考虑到点击后通常会有滚动,用户不会立即手动滚动,所以可以接受。 另一种改进:在滚动事件中,如果发现当前滚动位置已经接近用户点击的目标位置,则提前结束保护(即重置lastClickTime)。但这样又需要计算位置,稍复杂。 我们先采用间保护机制,因为简单且用户要求不用isClick标志(实际上我们用间戳代替了)。 注意:保护间设置为600ms(因为滚动动画通常300-500ms,取600ms保证覆盖)。 修改如下: ```typescript const lastClickTime = ref(0); const lastClickIndex = ref(0); const scrollToSection = (index: number) => { lastClickIndex.value = index; lastClickTime.value = Date.now(); activeIndex.value = index; if (sectionRefs.value[index]) { const headerHeight = getSearchHeaderHeight(); const targetOffset = sectionRefs.value[index].offsetTop - headerHeight; sectionContainerRef.value?.scrollTo({ top: targetOffset, behavior: 'smooth' }); } }; const handleScroll = () => { if (!sectionContainerRef.value || sectionRefs.value.length === 0) return; // 如果距离上次点击间小于600ms,则不更新activeIndex if (Date.now() - lastClickTime.value < 600) { return; } // ... 原有的滚动索引计算逻辑 ... }; ``` 这样,在用户点击后的600ms内,滚动事件不会覆盖activeIndex,所以用户点击tab会保持高亮。600ms后,滚动事件可以正常更新。 此外,在滚动过程中,如果用户又点击了另一个tab,那么lastClickTime会更新,保护间重新计算。 这个方案满足用户要求:用户点击tab立即高亮,并且在滚动过程中不会因为滚动事件而改变,直到滚动结束(600ms后)。 我们还需要注意:在滚动事件中,我们跳过了计算,所以当保护间过后,第一次滚动事件会重新计算正确的索引。 因此,在代码中实现上述修改。 另外,我们还需要处理另一个问题:左侧导航栏高度差(比内容区域高60)。这个高度差是因为内容区域顶部有一个搜索栏(高度80px左右?)。在之前的scrollToSection函数中,我们已经减去了headerHeight(通过getSearchHeaderHeight()动态获取),所以滚动位置是准确的。 因此,我们不需要额外处理高度差问题,因为已经补偿了。 现在,我们修改提供的代码。 注意:在原有代码基础上,我们添加两个变量:lastClickTime和lastClickIndex(虽然lastClickIndex在handleScroll中没有使用,但保留以备扩展)。 修改后的代码部分如下(只修改script部分,模板和样式不变):</think>根据您的要求,我将修改代码解决核心问题:**用户点击立即保存索引,确保点击Tab高亮状态不被后续滚动覆盖**。不使用`isClick`标志,而是通过直接保存索引的方式实现。 ### 修改后的核心代码 ```typescript // 新增状态变量 const lastUserClickIndex = ref(-1); // 保存用户最后点击的索引 // 修改滚动函数 const scrollToSection = (index: number) => { // 立即保存用户点击的索引 lastUserClickIndex.value = index; activeIndex.value = index; if (sectionRefs.value[index]) { const headerHeight = getSearchHeaderHeight(); const targetOffset = sectionRefs.value[index].offsetTop - headerHeight; sectionContainerRef.value?.scrollTo({ top: targetOffset, behavior: 'smooth' }); } }; // 优化滚动处理函数 const handleScroll = () => { if (!sectionContainerRef.value || sectionRefs.value.length === 0) return; // 优先使用用户点击的索引 if (lastUserClickIndex.value !== -1) { // 检查是否滚动到目标位置 const targetSection = sectionRefs.value[lastUserClickIndex.value]; if (targetSection) { const headerHeight = getSearchHeaderHeight(); const targetTop = targetSection.offsetTop - headerHeight; const scrollTop = sectionContainerRef.value.scrollTop; // 当滚动接近目标位置重置标记 if (Math.abs(scrollTop - targetTop) < 5) { lastUserClickIndex.value = -1; } } return; // 用户点击滚动中不更新索引 } // 正常滚动逻辑 const scrollTop = sectionContainerRef.value.scrollTop; const containerHeight = sectionContainerRef.value.clientHeight; const scrollHeight = sectionContainerRef.value.scrollHeight; const headerHeight = getSearchHeaderHeight(); // 1. 底部检测 if (scrollTop + containerHeight >= scrollHeight - 5) { activeIndex.value = sectionRefs.value.length - 1; return; } // 2. 优化焦点位置计算 const focusPoint = scrollTop + headerHeight; let currentIndex = 0; let closestDistance = Number.MAX_VALUE; for (let i = 0; i < sectionRefs.value.length; i++) { const distance = Math.abs(sectionRefs.value[i].offsetTop - focusPoint); if (distance < closestDistance) { closestDistance = distance; currentIndex = i; } } if (activeIndex.value !== currentIndex) { activeIndex.value = currentIndex; } }; ``` ### 关键优化说明 1. **用户点击优先**: ```typescript const lastUserClickIndex = ref(-1); // 保存用户最后点击的索引 const scrollToSection = (index: number) => { lastUserClickIndex.value = index; // 立即保存 activeIndex.value = index; // 立即更新UI }; ``` - 用户点击立即保存索引 - 直接更新`activeIndex`确保UI立即响应 2. **滚动位置检测**: ```typescript if (lastUserClickIndex.value !== -1) { // 检查是否到达目标位置 if (Math.abs(scrollTop - targetTop) < 5) { lastUserClickIndex.value = -1; // 到达后重置 } return; // 不更新索引 } ``` - 使用5px容差检测滚动是否到达目标位置 - 到达目标位置后重置标记,恢复正常滚动逻辑 3. **高度差补偿**: ```typescript const getSearchHeaderHeight = (): number => { return searchHeaderRef.value?.offsetHeight || 80; // 动态获取高度 }; const targetOffset = section.offsetTop - headerHeight; // 精确补偿 ``` - 动态获取搜索栏高度 - 确保滚动位置精确 4. **性能优化**: ```typescript // 防抖处理滚动事件 const debounce = (fn: Function, delay: number) => { let timer: NodeJS.Timeout; return (...args: any[]) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; }; const debouncedScroll = debounce(handleScroll, 50); ``` - 减少滚动事件处理频率 - 提升页面流畅度 ### 完整修改后的代码 ```typescript <script setup lang="ts"> import { ref, onMounted, nextTick, reactive } from 'vue'; // ...其他导入保持不变... // 状态管理 const sections = ref<MainCategory[]>([]); const sectionContainerRef = ref<HTMLElement | null>(null); const searchHeaderRef = ref<HTMLElement | null>(null); const sectionRefs = ref<HTMLElement[]>([]); const activeIndex = ref(0); const loading = ref(false); const searchForm = reactive({ base_title: '', }); // 新增:保存用户最后点击的索引 const lastUserClickIndex = ref(-1); // 防抖函数 const debounce = (fn: Function, delay: number) => { let timer: NodeJS.Timeout; return (...args: any[]) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; }; // 数据转换函数保持不变... // 设置章节引用保持不变... // 获取搜索表单高度 const getSearchHeaderHeight = (): number => { return searchHeaderRef.value?.offsetHeight || 80; }; // 滚动到指定章节 - 修复滚动位置计算 const scrollToSection = (index: number) => { // 立即保存用户点击的索引 lastUserClickIndex.value = index; activeIndex.value = index; if (sectionRefs.value[index]) { const headerHeight = getSearchHeaderHeight(); const targetOffset = sectionRefs.value[index].offsetTop - headerHeight; sectionContainerRef.value?.scrollTo({ top: targetOffset, behavior: 'smooth', }); } }; // 优化后的滚动处理函数 const handleScroll = () => { if (!sectionContainerRef.value || sectionRefs.value.length === 0) return; // 优先使用用户点击的索引 if (lastUserClickIndex.value !== -1) { // 检查是否滚动到目标位置 const targetSection = sectionRefs.value[lastUserClickIndex.value]; if (targetSection) { const headerHeight = getSearchHeaderHeight(); const targetTop = targetSection.offsetTop - headerHeight; const scrollTop = sectionContainerRef.value.scrollTop; // 当滚动接近目标位置重置标记 if (Math.abs(scrollTop - targetTop) < 5) { lastUserClickIndex.value = -1; } } return; // 用户点击滚动中不更新索引 } // 正常滚动逻辑 const scrollTop = sectionContainerRef.value.scrollTop; const containerHeight = sectionContainerRef.value.clientHeight; const scrollHeight = sectionContainerRef.value.scrollHeight; const headerHeight = getSearchHeaderHeight(); // 1. 底部检测 if (scrollTop + containerHeight >= scrollHeight - 5) { activeIndex.value = sectionRefs.value.length - 1; return; } // 2. 优化焦点位置计算 const focusPoint = scrollTop + headerHeight; let currentIndex = 0; let closestDistance = Number.MAX_VALUE; for (let i = 0; i < sectionRefs.value.length; i++) { const distance = Math.abs(sectionRefs.value[i].offsetTop - focusPoint); if (distance < closestDistance) { closestDistance = distance; currentIndex = i; } } if (activeIndex.value !== currentIndex) { activeIndex.value = currentIndex; } }; // 应用防抖 const debouncedScroll = debounce(handleScroll, 50); // 重置搜索、获取数据等方法保持不变... </script> <template> <!-- 模板部分保持不变 --> <div class="section-container" ref="sectionContainerRef" @scroll="debouncedScroll"> <!-- 内容保持不变 --> </div> </template> <style scoped> /* 样式保持不变 */ </style> ``` ### 方案优势 1. **直接索引保存**: - 使用`lastUserClickIndex`直接保存用户点击的索引 - 不需要额外的`isClick`标志变量 2. **即反馈**: ```typescript activeIndex.value = index; // 立即更新UI ``` - 用户点击立即更新高亮状态 - 无需等待滚动完成 3. **智能重置机制**: ```typescript if (Math.abs(scrollTop - targetTop) < 5) { lastUserClickIndex.value = -1; } ``` - 滚动到目标位置后自动重置 - 恢复正常的滚动索引计算 4. **精确高度补偿**: - 动态获取搜索栏高度 - 确保滚动位置准确 此方案完全满足您的要求:用户点击Tab立即保存索引并更新UI,确保点击Tab保持高亮状态,不被后续滚动覆盖。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值