You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jQuery单页菜单点击选中错误项问题求助

问题描述

单页内部菜单用jQuery实现滚动/点击时自动添加active类并更新URL,滚动功能正常,但点击菜单项时,会激活点击项上方的菜单并更新错误URL,无法排查原因。

菜单结构

<ul>
<li><a href="#1" data-id="1" class="toc-link">link 1</a></li>
<li><a href="#2" data-id="2" class="toc-link">link 2</a></li>
...etc
</ul>

内容结构

<h2 id="1">title 1</h2>
<p>content</p>
<h2 id="2">title 2</h2>
<p>content</p>
...etc

现有jQuery脚本

jQuery(document).ready(function() {
  // Update the active header and URL on click
  jQuery('.toc-link').click(function(e) {
    e.preventDefault();
    var activeId = jQuery(this).attr('data-id');
    var url = window.location.href.split('#')[0];
    var newUrl = url + '#' + activeId;
    window.history.pushState('', document.title, newUrl);

    jQuery('.toc-link.active').removeClass('active');
    jQuery(this).addClass('active');
    
    // Scroll to corresponding header
    var headerTop = jQuery('#' + activeId).offset().top;
    jQuery('html, body').animate({ scrollTop: headerTop }, 500);
  });

  // Update the active header and URL on scroll
  jQuery(window).scroll(function() {
    var scrollPos = jQuery(window).scrollTop();
    var activeId = null; // Initialize activeId to null

    jQuery('h2').each(function() {
      var headerTop = jQuery(this).offset().top;
      var id = jQuery(this).attr('id');

      if (scrollPos >= headerTop) {
        activeId = id;
      }
    });

    if (activeId !== null) {
      var newUrl = window.location.href.split('#')[0] + '#' + activeId;
      window.history.pushState('', document.title, newUrl);

      jQuery('.toc-link').removeClass('active'); // remove active class from all links
      jQuery('.toc-link[data-id="' + activeId + '"]').addClass('active'); // add active class to matching link
    }
  });
});

问题原因与修复方案

问题核心:点击触发滚动动画后,scroll事件会被多次触发,覆盖点击时设置的active状态和URL。滚动逻辑遍历所有h2时,会把最后一个满足scrollPos >= headerTop的元素设为activeId,滚动到目标的过程中会先经过上方的h2,导致最终状态错误。

修复后的脚本

jQuery(document).ready(function() {
  let isScrollingAnimated = false; // 标记是否在执行点击触发的滚动动画

  // 点击事件处理
  jQuery('.toc-link').click(function(e) {
    e.preventDefault();
    const activeId = jQuery(this).attr('data-id');
    const url = window.location.href.split('#')[0];
    const newUrl = url + '#' + activeId;
    
    // 直接更新URL和active状态
    window.history.pushState('', document.title, newUrl);
    jQuery('.toc-link.active').removeClass('active');
    jQuery(this).addClass('active');
    
    // 标记动画开始,阻止滚动事件干扰
    isScrollingAnimated = true;
    const headerTop = jQuery('#' + activeId).offset().top;
    jQuery('html, body').animate({ scrollTop: headerTop }, 500, function() {
      // 动画结束后取消标记,同步最终状态
      isScrollingAnimated = false;
      updateActiveOnScroll();
    });
  });

  // 提取滚动逻辑为独立函数
  function updateActiveOnScroll() {
    if (isScrollingAnimated) return; // 动画期间跳过执行

    const scrollPos = jQuery(window).scrollTop();
    let activeId = null;

    // 反向遍历h2,找到第一个满足条件的元素(当前视口最顶部的激活项)
    jQuery('h2').each(function(index, element) {
      const headerTop = jQuery(element).offset().top;
      const id = jQuery(element).attr('id');
      // 添加20px偏移量,避免触顶频繁切换状态
      if (scrollPos >= headerTop - 20) {
        activeId = id;
        return false; // 找到后停止遍历
      }
    });

    if (activeId !== null) {
      const currentHash = window.location.hash.slice(1);
      // 仅当hash变化时更新,避免重复操作历史记录
      if (currentHash !== activeId) {
        const newUrl = window.location.href.split('#')[0] + '#' + activeId;
        window.history.pushState('', document.title, newUrl);
      }
      jQuery('.toc-link').removeClass('active');
      jQuery('.toc-link[data-id="' + activeId + '"]').addClass('active');
    }
  }

  // 绑定滚动事件
  jQuery(window).scroll(updateActiveOnScroll);
  // 页面加载时初始化状态
  updateActiveOnScroll();
});

优化说明

  • 新增动画状态标记,避免滚动事件在点击动画期间干扰状态
  • 反向遍历h2,确保选中当前视口内最顶部的目标项
  • 添加偏移量,减少滚动时的状态频繁切换
  • 仅在hash实际变化时更新历史记录,避免冗余操作
  • 页面加载时主动初始化状态,确保初始显示正确

内容的提问来源于stack exchange,提问作者Natan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 03:26:18