在页面滚动div时,突出显示活动菜单项(侧边栏滚动菜单)

4
这个JSFiddleGaurav Kalyan创建,在Chrome中运行良好,但在Safari和Firefox中会激活错误的菜单项。它不是突出显示点击的菜单项,而是突出显示之前的菜单项。举个例子,如果你点击“Punkt 4”,则“Punkt 3”会被突出显示。我一直无法解决这个问题,有人能帮忙吗?我已经尝试解决了两周。
HTML
    <section id="main">
    <div class="target" id="1">TARGET 1</div>
    <div class="target" id="2">TARGET 2</div>
    <div class="target" id="3">TARGET 3</div>
    <div class="target" id="4">TARGET 4</div>
</section>
<aside id="nav">
    <nav>
        <a href="#1" class="active">Punkt 1</a>
        <a href="#2">Punkt 2</a>
        <a href="#3">Punkt 3</a>
        <a href="#4">Punkt 4</a>
    </nav>
</aside>

CSS

    * {
    margin: 0;
    padding: 0;
}

#main {
    width: 75%;
    float: right;
}

#main div.target {
    background: #ccc;
    height: 400px;
}

#main div.target:nth-child(even) {
    background: #eee;
}

#nav {
    width: 25%;
    position: relative;
}

#nav nav {
    position: fixed;
    width: 25%;
}

#nav a {
    border-bottom: 1px solid #666;
    color: #333;
    display: block;
    padding: 10px;
    text-align: center;
    text-decoration: none;
}

#nav a:hover, #nav a.active {
    background: #666;
    color: #fff;
}

JavaScript

    $('#nav nav a').on('click', function(event) {
    $(this).parent().find('a').removeClass('active');
    $(this).addClass('active');
});

$(window).on('scroll', function() {
    $('.target').each(function() {
        if($(window).scrollTop() >= $(this).offset().top) {
            var id = $(this).attr('id');
            $('#nav nav a').removeClass('active');
            $('#nav nav a[href=#'+ id +']').addClass('active');
        }
    });
});

它在我的Firefox 61.0上运行良好。您可能需要使用“Ctrl + F5”刷新页面。 - Mohamed-Yousef
这有点具有欺骗性,@Mohamed-Yousef -- 它可以在顶部菜单项上工作,但是例如,如果您按“Punkt 4”,则会突出显示“Punkt 3”。(我在Mac上运行Firefox 65.0.1。) - Saul OGrady
1个回答

1
如果视口高度(浏览器窗口的内部高度)小于或等于400像素,则此代码可以正常工作。这是因为当您单击具有href =“#4”的nav元素中的a链接时,默认的浏览器行为会启动,并将id =“4”的元素滚动到顶部(尽可能多)。
当视口的高度与要滚动到的元素相同或更小时,当触发滚动处理程序时,if($(window).scrollTop()> = $(this).offset()。top)条件将计算为true,因为scrollTop将完全等于#4 div的offset()。top。
然而,当视口比内容div(在您的情况下,> 400px)更大时,当浏览器尝试将最后一个div滚动到视图中时,它可以完全这样做,同时仍然显示前一个div底部的一部分。这意味着第三个div将通过您的滚动处理程序的if检查,而不是第四个div。(最后一个div的offset top将不会<=窗口的scrollTop)。

example of viewport height more than 400 pixels

那么解决方案是什么?

我会让每个目标div的高度至少与视口相同。你可以在现代浏览器上使用min-height: 100vh;(视口高度的100%)来实现这一点。这意味着当最后一个div滚动到视图中时,它将完全填充视口,并且正确的div将正确地通过你的scroll逻辑检查。

请点击此处查看工作分支。

额外提示

有很多事情可以做来提高此代码的性能。缓存jQuery变量的创建,避免每次滚动事件发生时重复执行4次的操作(这可能经常发生),等等。它现在运行得还不错,但以后可能会成为瓶颈。


1
非常感谢您的指引,@GregL。我已经尝试在我正在开发的一个项目(带有平滑滚动)中加入“min-height: 100vh”。虽然“min-height:100vh”解决了最紧急的问题,但我在Safari和移动浏览器上遇到了进一步的问题。非常感谢您的帮助,非常感激。 - Saul OGrady
1
对于非jQuery代码,使用window.scrollY > elm.offsetTop,我喜欢减去一个值来调整,以便不完全位于顶部。如果你想要减去屏幕高度的一半,则可以使用以下代码:if (window.scrollY >= elm.offsetTop - window.innerHeight / 2) - Justin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接