如何在服务器端浏览器中捕获window.onscroll以模仿引导滚动间谍

问题描述 投票:0回答:1

我正在构建我的第一个服务器端blazor应用。我正在使用bootstrap css进行样式设计。我没有使用引导脚本。我的页面顶部固定有导航栏。我需要模仿引导滚动间谍。为此,我需要使用C#监视window.onscroll来控制滚动位置,并将所需的CSS类应用于nav-item

这是我的HTML:

_ Host.cshtml:

@page "/"
@namespace Web.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Learn Blazor</title>
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/animate/animate.css">
    <link rel="stylesheet" href="~/css/font-awesome/css/all.min.css" />
    <link rel="stylesheet" href="~/css/style.css">
</head>
<body data-spy="scroll" data-target=".site-nav" data-offset="55">
    <app>
        <component type="typeof(App)" render-mode="ServerPrerendered" />
    </app>

    <script src="_framework/blazor.server.js"></script>
    <script src="~/js//script.js"></script>
</body>
</html>

Index.razor:

@page "/"

<HomeSection></HomeSection>

<ColumnSection></ColumnSection>

<MediaSection></MediaSection>

家庭组件:

<header id="page-hero" class="site-header">

    <TopNavMenu></TopNavMenu>

    <section class="layout-hero d-flex align-items-center text-light text-center">
        <div class="container">
            <div class="row justify-content-center">
                <div class="col-11 col-sm-10 col-md-8 animated fadeInUp">
                    <h3 class="page-section-title">Learn Blazor</h3>
                    <p class="page-section-text">
                        My First Blazor App
                    </p>
                </div>
            </div>
        </div>
    </section>

</header>

TopNavMenu组件:

@inherits TopNavMenuBase

<nav class="site-nav family-sans text-uppercase navbar navbar-expand-md navbar-dark fixed-top" @ref="headerNav">

    <div class="container-fluid">

        <NavLink class="navbar-brand" href="#page-hero" @onclick="NavigateToElementAsync">
            <i class="fas fa-microphone-alt"></i> The Prodcast
        </NavLink>

        <button type="button" class="navbar-toggler @NavToggleCssClass" @onclick="ToggleNavMenu" aria-controls="#myTogglerNav"
                aria-label="Toggle Navigation" aria-expanded="@($"{!collapseNavMenu}".ToLowerInvariant())">
            <span class="navbar-toggler-icon"></span>
        </button>

        <section class="navbar-collapse collapse @NavMenuCssClass" id="myTogglerNav">
            <div class="navbar-nav ml-auto">
                <NavLink class="nav-item nav-link" href="#page-hero" @onclick="NavigateToElementAsync">home</NavLink>
                <NavLink class="nav-item nav-link" href="#page-multicolumn" @onclick="NavigateToElementAsync">columns</NavLink>
                <NavLink class="nav-item nav-link" href="#page-media" @onclick="NavigateToElementAsync">media</NavLink>
               </div>
        </section>

    </div>

</nav>

这是我的ComponentBase:

public class TopNavMenuBase : ComponentBase
{
    protected ElementReference headerNav;
    protected bool collapseNavMenu = true;
    protected string NavToggleCssClass => collapseNavMenu ? "collapsed" : null;
    protected string NavMenuCssClass => !collapseNavMenu ? "show" : null;

    [Inject]
    public IJSRuntime JSRuntime { get; set; }

    [Inject]
    public NavigationManager NavigationManager { get; set; }

    protected void ToggleNavMenu() => collapseNavMenu = !collapseNavMenu;

    protected override async Task OnAfterRenderAsync(bool firstRender) => await NavigateToElementAsync();

    protected async Task NavigateToElementAsync()
    {
        var fragment = new Uri(NavigationManager.Uri).Fragment;
        var elementId = !string.IsNullOrEmpty(fragment) && fragment.StartsWith("#") ? fragment.Substring(1) : fragment;

        await JSRuntime.InvokeAsync<bool>("scrollToElementId", elementId, headerNav);
    }
}

最后是我的脚本:

编辑1:更新了完整的工作内容window.onscroll,以使用纯JavaScript进行滚动监视

function scrollToElementId(target, headerNav) {
    var topoffset = 55;
    var element = document.getElementById(target);
    if (element) {
        const bodyRect = document.body.getBoundingClientRect().top;
        const elementRect = element.getBoundingClientRect().top;
        const elementPosition = elementRect - bodyRect;
        const offsetPosition = elementPosition - topoffset;

        window.scrollTo({
            top: offsetPosition,
            behavior: 'smooth'
        });

        headerNav.classList.toggle('inbody', target !== 'page-hero');

        return true;
    }

    document.querySelector('a.nav-link[href="#page-hero"]').classList.toggle('active', target === '');

    return false;
}

var sections = {};

document.querySelectorAll(".page-section,.site-header").forEach(section => sections[section.id] = section.offsetTop);

window.onscroll = function () {
    document.querySelector('header nav').classList.toggle('inbody', document.documentElement.scrollTop > 380);
    document.querySelector('#page-media .layout-animation').style['visibility'] = 'hidden';

    var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;

    Object.keys(sections).forEach(key => {
        if (sections[key] <= scrollPosition) {
            document.querySelectorAll('a.nav-item').forEach(a => a.classList.remove('active'));
            document.querySelector('a[href="#' + key + '"]').classList.add('active');

            if (key === 'page-media') {
                document.querySelector('#page-media .layout-animation').classList.add('animated', 'fadeInRight');
            }
        }
    });
};

以上代码设置有效。但是我觉得使用javascript捕获window.onscroll不是理想的解决方案。使用服务器端Blazor,我想使用C#做到这一点。但是如何用window.onscroll捕获此@onscroll?我需要在哪里钩住此@onscrollbody标记位于_Host.cshtml中,并且TopNavMenu组件嵌套在Home组件中,该组件是从index.razor文件渲染的。

有人可以协助我如何在Blazor服务器端使它正常工作吗?如果我错了,请纠正我。

c# bootstrap-4 blazor blazor-server-side scrollspy
1个回答
0
投票

我终于使用javascript中的IntersectionObserver使它正常工作。

我替换了window.onscroll函数,因为它的性能令人讨厌highlightMenu()函数

function highlightMenu() {
    const sections = document.querySelectorAll('.page-section,.site-header');
    const config = {
        rootMargin: '-55px 0px -85%'
    };

    let observer = new IntersectionObserver(function
        (entries, self) {

        entries.forEach(entry => {
            if (entry.isIntersecting) {
                intersectionHandler(entry);
            }
        });
    }, config);

    sections.forEach(section => observer.observe(section));
}

function intersectionHandler(entry) {
    const id = entry.target.id;
    document.querySelector('header nav').classList.toggle('inbody', id !== 'page-hero');
    if (id === 'page-media') {
        document.querySelector('#page-media .layout-animation').classList.add('animated', 'fadeInRight');
    }
    const currentlyActive = document.querySelector('nav a.nav-item.active');
    const shouldBeActive = document.querySelector('nav a.nav-item[href="#' + id + '"]');

    if (currentlyActive) {
        currentlyActive.classList.remove('active');
    }
    if (shouldBeActive) {
        shouldBeActive.classList.add('active');
    }
}

然后我从OnAfterRenderAsyncIndex.Razor内部调用了此函数,

@code{

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSRuntime.InvokeVoidAsync("highlightMenu");
        }
    }

}

它像自举式间谍一样工作。希望这对某人有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.