向下滚动到部分时突出显示菜单项

2023-12-22

我知道这个问题在这个论坛上已经被问了一百万次,但没有一篇文章帮助我找到解决方案。

我编写了一小段 jquery 代码,当您向下滚动到与哈希链接中​​具有相同 id 的部分时,它会突出显示哈希链接。

$(window).scroll(function() {
    var position = $(this).scrollTop();

    $('.section').each(function() {
        var target = $(this).offset().top;
        var id = $(this).attr('id');

        if (position >= target) {
            $('#navigation > ul > li > a').attr('href', id).addClass('active');
        }
    });
});

现在的问题是,它突出显示了所有哈希链接,而不仅仅是与该部分相关的哈希链接。谁能指出这个错误,或者是我忘记了什么?


EDIT:

我修改了我的答案,谈论一些性能和一些特殊情况。

如果您在这里只是寻找代码,底部有一个带注释的片段。


原答案

而不是添加.active class对于所有链接,您应该确定哪个属性href与该部分的相同id.

然后你可以添加.active class到该链接并将其从其余链接中删除。

        if (position >= target) {
            $('#navigation > ul > li > a').removeClass('active');
            $('#navigation > ul > li > a[href=#' + id + ']').addClass('active');
        }

通过上述修改,您的代码将正确突出显示相应的链接。希望能帮助到你!


提高绩效

即使这段代码能够完成其工作,也远非最佳。无论如何,请记住:

我们应该忘记小效率,大约 97% 的情况下: 过早的优化是万恶之源。然而我们不应该通过 在那关键的 3% 中增加我们的机会。 (唐纳德·高德纳)

因此,如果在慢速设备中进行事件测试,您没有遇到性能问题,那么您能做的最好的事情就是停止阅读并考虑您的项目的下一个令人惊叹的功能!

基本上可以通过三个步骤来提高性能:

尽可能多地进行前期工作:

为了避免一次又一次地搜索 DOM(每次触发事件时),您可以预先缓存 jQuery 对象(例如,在document.ready):

var $navigationLinks = $('#navigation > ul > li > a');
var $sections = $(".section"); 

然后,您可以将每个部分映射到相应的导航链接:

var sectionIdTonavigationLink = {};
$sections.each( function(){
    sectionIdTonavigationLink[ $(this).attr('id') ] = $('#navigation > ul > li > a[href=\\#' + $(this).attr('id') + ']');
});

注意锚选择器中的两个反斜杠:散列'#' 在 CSS 中有特殊含义,所以必须逃脱 https://api.jquery.com/category/selectors/(谢谢@Johnnie https://stackoverflow.com/a/46554013/5247200).

另外,您可以缓存每个部分的位置(Bootstrap 的滚动间谍 http://v4-alpha.getbootstrap.com/components/scrollspy/可以)。但是,如果您这样做,您需要记住每次更改时更新它们(用户调整窗口大小、通过 ajax 添加新内容、展开小节等)。

优化事件处理程序:

想象一下用户正在滚动inside一节:活动导航链接不需要更改。但如果你看一下上面的代码,你会发现它实际上改变了好几次。在突出显示正确的链接之前,所有先前的链接也会执行此操作(因为它们相应的部分也验证了条件position >= target).

一种解决方案是从底部到顶部迭代各个部分,第一个部分的.offset().top等于或小于$(window).scrollTop是正确的。是的,你可以依靠 jQuery 按 DOM 的顺序返回对象 https://stackoverflow.com/a/25165306/5247200 (since 版本1.3.2 http://blog.jquery.com/2009/02/20/jquery-1-3-2-released/)。要从下到上迭代,只需按相反顺序选择它们:

var $sections = $( $(".section").get().reverse() );
$sections.each( ... );

$()是必要的,因为get()返回 DOM 元素,而不是 jQuery 对象。

一旦找到正确的部分,您应该return false退出循环并避免检查更多部分。

最后,如果正确的导航链接已经突出显示,您不应该执行任何操作,因此请检查一下:

if ( !$navigationLink.hasClass( 'active' ) ) {
    $navigationLinks.removeClass('active');
    $navigationLink.addClass('active');
}

尽可能少地触发事件:

防止高评级事件(滚动、调整大小...)使您的网站缓慢或无响应的最明确方法是控制事件处理程序的调用频率:确保您不需要检查需要突出显示哪个链接每秒100次!如果,除了链接突出显示之外,您添加一些奇特的视差效果,您可以快速解决介绍麻烦。

此时,您肯定想了解有关throttle、debounce 和requestAnimationFrame 的内容。本文 https://css-tricks.com/debouncing-throttling-explained-examples/这是一个很好的讲座,给你一个关于其中三个的很好的概述。对于我们的情况,节流最适合我们的需求。

基本上,限制会强制执行两个函数执行之间的最小时间间隔。

我在代码片段中实现了节流函数。从那里你可以变得更复杂,甚至更好,使用像这样的库下划线.js http://underscorejs.org/ or lodash https://lodash.com/(如果您不需要整个库,您可以随时从那里提取节流函数)。

注意:如果你环顾四周,你会发现更多简单的节流功能。小心他们,因为他们可能会错过最后一个事件触发器(这是最重要的一个!)。

特殊情况:

我不会将这些案例包含在代码片段中,以免使其进一步复杂化。

在下面的代码片段中,当该部分到达页面的最顶部时,链接将突出显示。如果你想让它们之前突出显示,你可以通过这种方式添加一个小的偏移量:

if (position + offset >= target) {

当您有顶部导航栏时,这特别有用。

如果您的最后一个部分太小而无法到达页面顶部,您可以在滚动条位于最底部位置时突出显示其相应的链接:

if ( $(window).scrollTop() >= $(document).height() - $(window).height() ) {
    // highlight the last link

有一些浏览器支持问题的想法。您可以阅读更多相关内容here https://stackoverflow.com/q/3898130/5247200 and here https://stackoverflow.com/q/9439725/5247200.

片段和测试

最后,这里有一个评论片段。请注意,我更改了一些变量的名称,以使它们更具描述性。

// cache the navigation links 
var $navigationLinks = $('#navigation > ul > li > a');
// cache (in reversed order) the sections
var $sections = $($(".section").get().reverse());

// map each section id to their corresponding navigation link
var sectionIdTonavigationLink = {};
$sections.each(function() {
    var id = $(this).attr('id');
    sectionIdTonavigationLink[id] = $('#navigation > ul > li > a[href=\\#' + id + ']');
});

// throttle function, enforces a minimum time interval
function throttle(fn, interval) {
    var lastCall, timeoutId;
    return function () {
        var now = new Date().getTime();
        if (lastCall && now < (lastCall + interval) ) {
            // if we are inside the interval we wait
            clearTimeout(timeoutId);
            timeoutId = setTimeout(function () {
                lastCall = now;
                fn.call();
            }, interval - (now - lastCall) );
        } else {
            // otherwise, we directly call the function 
            lastCall = now;
            fn.call();
        }
    };
}

function highlightNavigation() {
    // get the current vertical position of the scroll bar
    var scrollPosition = $(window).scrollTop();

    // iterate the sections
    $sections.each(function() {
        var currentSection = $(this);
        // get the position of the section
        var sectionTop = currentSection.offset().top;

        // if the user has scrolled over the top of the section  
        if (scrollPosition >= sectionTop) {
            // get the section id
            var id = currentSection.attr('id');
            // get the corresponding navigation link
            var $navigationLink = sectionIdTonavigationLink[id];
            // if the link is not active
            if (!$navigationLink.hasClass('active')) {
                // remove .active class from all the links
                $navigationLinks.removeClass('active');
                // add .active class to the current link
                $navigationLink.addClass('active');
            }
            // we have found our section, so we return false to exit the each loop
            return false;
        }
    });
}

$(window).scroll( throttle(highlightNavigation,100) );

// if you don't want to throttle the function use this instead:
// $(window).scroll( highlightNavigation );
#navigation {
    position: fixed;
}
#sections {
    position: absolute;
    left: 150px;
}
.section {
    height: 200px;
    margin: 10px;
    padding: 10px;
    border: 1px dashed black;
}
#section5 {
    height: 1000px;
}
.active {
    background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="navigation">
    <ul>
        <li><a href="#section1">Section 1</a></li>
        <li><a href="#section2">Section 2</a></li>
        <li><a href="#section3">Section 3</a></li>
        <li><a href="#section4">Section 4</a></li>
        <li><a href="#section5">Section 5</a></li>
    </ul>
</div>
<div id="sections">
    <div id="section1" class="section">
        I'm section 1
    </div>
    <div id="section2" class="section">
        I'm section 2
    </div>
    <div id="section3" class="section">
        I'm section 3
    </div>
    <div id="section4" class="section">
        I'm section 4
    </div>
    <div id="section5" class="section">
        I'm section 5
    </div>
</div>

如果你有兴趣,这把小提琴 https://jsfiddle.net/upqwhou2/测试我们讨论过的不同改进。

快乐编码!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

向下滚动到部分时突出显示菜单项 的相关文章

随机推荐

  • 使用 Spring 在 Swagger UI 上收到 404 错误

    我正在将 swagger UI 与 Spring boot 应用程序集成 当我点击 swagger ui html 时 我收到 404 错误 我的配置类如下 Configuration EnableSwagger2 Import Swagg
  • 有条件地从列表中获取总和

    我有一个类属性详细信息 public class PropertyDetails public int Sequence get set public int Length get set public string Type get se
  • NightwatchJS 和 WebdriverIO 有什么区别?

    正如标题所述 Nightwatch js 和 Webdriver io 有什么区别 看起来它们具有相同的语法并且做几乎相同的事情 它们有何不同 我需要在他们之间做出选择 我已经多次使用这些工具编写了测试套件 Webdriver io 允许您
  • 出现错误 - 运行本机反应时无法识别的命令“run-android”,

    当尝试在我的模拟器上运行本机反应时 我收到此错误 react native run android 错误无法识别的命令 run android 我的模拟器已连接并且正在运行 有什么想法吗 包 json name iaapp version
  • 全屏意图不启动 Activity,但在 Android 10 上显示通知

    我尝试使用下一个代码启动广播接收器的活动 Intent i new Intent context AlarmNotification class i setFlags Intent FLAG ACTIVITY NEW TASK Intent
  • 如何调整轴以从 r 图中的零原点开始

    为了绘制三个变量 x1 x2 和 x3 的经验累积密度 我在 r 中使用了以下内容 plot ecdf x1 col blue main Distribution XYZ xlab x i ylab Prob x i lt y lines
  • 匹配点的正则表达式

    想知道最好的匹配方式是什么 test this from blah blah blah email protected cdn cgi l email protection blah blah 是 使用Python 我试过了re split
  • 如何获取Android系统颜色?

    我正在寻找获取 Android 系统颜色 设备中使用的颜色主题的方法 Using android color 我没有得到正确的颜色 例如 我的设备中的背景颜色是BLACK 菜单背景颜色为DARKGREY 值来自android color 在
  • 如何在代码中处理游标上的 IllegalStateException?

    当我调试我的应用程序时突然弹出此错误 我该如何处理这种错误 我不知道是在哪里以及如何引起的 Daemon System Thread lt 5 gt HeapWorker Suspended exception IllegalStateEx
  • Pytorch 中缺乏 L1 正则化的稀疏解决方案

    我正在尝试在简单神经网络的第一层 1 个隐藏层 上实现 L1 正则化 我查看了 StackOverflow 上的其他一些帖子 这些帖子使用 Pytorch 应用 l1 正则化来弄清楚应该如何完成 参考文献 在 PyTorch 中添加 L1
  • 使用 RSpec 进行改进的测试类

    假设我已经精炼了 module RefinedString refine String do def remove latin letters code code code code end end end 我在课堂演讲中使用它 class
  • 将所有“工作表对象”转换为 powerpoint 中的图像

    真的不知道把它放在哪个堆栈站点上 请随意将其移至正确的位置 我的问题与编程并不真正相关 但我有大量的幻灯片中嵌入了这些 工作表对象 的要点 有些似乎是来自 Excel 的图表以及来自 Visio 的其他图表类型项目 我需要将所有这些 工作表
  • Kivy 规则继承与 add_widget()

    跟进问题 Kivy 外部规则固有 https stackoverflow com questions 31618565 kivy outside rule inherence main py from kivy app import App
  • 为什么 Azure 不在我的两个实例之一上调度 HTTP 请求?

    我有一个带有两个实例的 Azure Web 角色 两个实例都 准备就绪 运行正常 在我的桌面上 同一程序的四个实例同时运行 并通过 HTTP 请求访问 Web 角色 URL 但根据日志 所有请求仅分派到实例 0 我需要将请求分派到两个实例以
  • C# 通用列表联合问题

    我正在尝试使用 Union 合并两个列表 以便消除重复项 以下是示例代码 public class SomeDetail public string SomeValue1 get set public string SomeValue2 g
  • ipython 笔记本中的居中对齐输出

    我想将我的输出 包括文本和绘图 居中对齐ipython notebook 有没有一种方法可以在同一个笔记本中添加样式 代码或屏幕截图示例会有很大帮助 尝试在代码单元中运行此命令以覆盖输出单元的默认 CSS from IPython disp
  • SharePoint 2010:RemoveFieldRef 和 Inherits="TRUE"

    我创建了一个继承自 OOTB SharePoint 的自定义内容类型Picture内容类型 我所做的唯一自定义是添加一个简单的 URL 字段 并删除基本类型上的两个字段 见下文
  • 如何根据编译器类型在 C 编译器的 SConstruct 中设置选项?

    我需要为 C 编译器设置附加选项 例如添加标志以打开所有警告 具体取决于编译器的类型 例如 对于 MSVC 我应该使用 env Append CPPFLAGS Wall 但对于 mingw gcc 我需要使用 env Append CCFL
  • Symfony2 异常响应。将 404 状态代码替换为 200

    我在 symfony2 中有一个 404 处理程序 它是一个 EventListener 对于某些 404 我会进行重定向 效果很好 对于浏览器来说 不会抛出 404 错误 new RedirectResponse newURL 该行基本上
  • 向下滚动到部分时突出显示菜单项

    我知道这个问题在这个论坛上已经被问了一百万次 但没有一篇文章帮助我找到解决方案 我编写了一小段 jquery 代码 当您向下滚动到与哈希链接中 具有相同 id 的部分时 它会突出显示哈希链接 window scroll function v