单击 Blazor 中的顶部导航链接时滚动到页面的指定部分

Posted

技术标签:

【中文标题】单击 Blazor 中的顶部导航链接时滚动到页面的指定部分【英文标题】:Scroll to specified part of page when clicking top navigation link in Blazor 【发布时间】:2019-08-06 18:43:26 【问题描述】:

如何在 Blazor 中对已加载页面进行简单的“跳转”部分?像这样在 html 中:

<a href="#contact">Contact us</a>
...
<section id="contact">

理想情况下,我还希望它平滑地向下滚动到此部分。以为我会尝试用 CSS 解决这个问题,但也许不可能?

【问题讨论】:

Autofocus on <a> element without javascript的可能重复 @BabakNaffas 该技术通常不适用于 Blazor 等 SPA 应用程序。 谢谢@KirkWoll。很高兴知道。 【参考方案1】:

您需要的是 Blazor 的散列路由功能。但是,唉,这样的功能还不存在。我建议您使用 JSIterop 来执行此任务:创建一个执行导航的 JavaScript,并将其传递给 ElementRef 对象。

希望对你有帮助……

编辑:以下是我在 Github 中找到的最佳解决方案的改编...

通常,当您单击联系链接时,您会被重定向到路由http://localhost:5000/mypage#contact,但会位于页面顶部。路由的片段不用于选择特定的 HTML 元素。

当前的解决方法是编写解释 URL 的显式代码。在上面的示例中,我们可以使用一点 JavaScript,然后从我们的 Blazor 代码中调用它:

mypage.cshtml:

         @page "/mypage"
    @inject Microsoft.AspNetCore.Components.Services.IUriHelper UriHelper

    <nav>

        <a href="#contact">contact</a>


    </nav>


    <section>
        <h2 id="contact">contact</h2>

    </section>


@functions 
    protected override void OnInit()
    
        NavigateToElement();
        UriHelper.OnLocationChanged += OnLocationChanges;
    

    private void OnLocationChanges(object sender, string location) => NavigateToElement();

    private void NavigateToElement()
    
        var url = UriHelper.GetAbsoluteUri();
        var fragment = new Uri(url).Fragment;

        if(string.IsNullOrEmpty(fragment))
        
            return;
        

        var elementId = fragment.StartsWith("#") ? fragment.Substring(1) : fragment;

        if(string.IsNullOrEmpty(elementId))
        
            return;
        

        ScrollToElementId(elementId);
    

    private static bool ScrollToElementId(string elementId)
    
        return JSRuntime.Current.InvokeAsync<bool>("scrollToElementId", elementId).GetAwaiter().GetResult();
    

index.html:

<script>
        window.scrollToElementId = (elementId) => 
            console.info('scrolling to element', elementId);
            var element = document.getElementById(elementId);
            if(!element)
            
                console.warn('element was not found', elementId);
                return false;
            
            element.scrollIntoView();
            return true;
        
</script>

注意:如果您使用的是 Blazor 版本 .9.0,则应注入 IJSRuntime 请让我知道此解决方案是否适合您...

【讨论】:

不错的 Issac,记住 JSRuntime.Current 不再受支持。现在 ( >= 0.9.0 ) 你必须通过 DI 实例化 JSRuntime。 :) 试过了。 Microsoft Edge 滚动到正确的区域,然后当我单击导航中的其他链接时没有反应。 Chrome 和 Firefox 根本不滚动,点击导航中的其他链接时也没有任何反应。真的很感谢你的努力!如果其他人尝试过会很好,所以我知道我的网站中是否有其他东西导致问题。请注意,我正在运行 Blazor 0.7.0 我现在已经在 Blazor 0.9.0 中尝试过了。使用创建新 Blazor 项目时创建的默认项目。只是想看看我的网站中是否有什么奇怪的东西阻止了它的工作。也没有在那里工作。在您找到它的 github 页面中,我看到他评论了“建议的解决方法不起作用,因为浏览器崩溃并且仅用于说明。”所以我猜我们只需要等待。【参考方案2】:

和Issac的回答一样,但是需要改一些代码。

我发现主要问题是您需要它是异步的。 @johajan

@inject IJSRuntime JSRuntime

...

@functions 
protected override async Task OnInitAsync()

    await base.OnInitAsync();
    //NavigateToElement();
    UriHelper.OnLocationChanged += OnLocationChanges;


private async Task OnLocationChanges(object sender, string location) => await NavigateToElement();

private async Task NavigateToElement()

    var url = UriHelper.GetAbsoluteUri();
    var fragment = new Uri(url).Fragment;

    if(string.IsNullOrEmpty(fragment))
    
        return;
    

    var elementId = fragment.StartsWith("#") ? fragment.Substring(1) : fragment;

    if(string.IsNullOrEmpty(elementId))
    
        return;
    

    await ScrollToElementId(elementId);


private Task<bool> ScrollToElementId(string elementId)

    return JSRuntime.InvokeAsync<bool>("scrollToElementId", elementId);

【讨论】:

【参考方案3】:

我通过使用button 然后在标记中编写一些内联Javascript 解决了这个问题。您可以将此推广到 Blazor 组件以获得奖励积分!

<button type="button" onclick="document.getElementById('contact').scrollIntoView(behavior:'smooth')">Contact us</button>
...
<section id="contact">

【讨论】:

谢谢,这几乎为我解决了!请参阅我的解决方案以添加到此解决方案。 我看不出与您的回答有何不同,但很高兴它让您走上了正轨【参考方案4】:

我使用了 Arron Hudon 的答案,但仍然没有用。然而,在玩了之后,我意识到它不适用于锚元素:&lt;a id='star_wars'&gt;Place to jump to&lt;/a&gt;。显然 Blazor 和其他 spa 框架在跳转到同一页面上的时存在问题。为了解决这个问题,我不得不改用段落元素(部分也可以):&lt;p id='star_wars'&gt;Some paragraph&lt;p&gt;

使用引导程序的示例:

<button class="btn btn-link" onclick="document.getElementById('star_wars').scrollIntoView(behavior:'smooth')">Star Wars</button>

... lots of other text

<p id="star_wars">Star Wars is an American epic...</p>

请注意,我使用 bootstrap 的 btn-link 类使按钮看起来像一个超链接。

【讨论】:

【参考方案5】:

你可以使用 2 个 Nuget 包来解决这个问题:

    Scroll JS 是 JsInterop Nuget 的一部分。这有很多功能,请参阅文档,但主要部分是 IScrollHandler,这是一个可注入服务。您可以使用demo app 进行尝试。可以看到scrollIntoView()等JS函数包裹,平滑滚动可用。使用 JS 滚动支持要简单得多... 第二个选项是在您拥有“#”的 URL 中使用“Parmalinks”。 Nuget 在这里可用。这是你要求的。基本上它使用&lt;a&gt; 标签,但您不必费心(注意即使是演示应用程序也有# URL)。还呈现带有可点击图标和导航/复制操作的“链接”组件。当前平滑滚动不可用,但可以请求。您可以通过demo app 试用。

【讨论】:

以上是关于单击 Blazor 中的顶部导航链接时滚动到页面的指定部分的主要内容,如果未能解决你的问题,请参考以下文章

jQuery单击链接时从页面顶部滚动x像素

滚动动画会破坏页面上的链接

滑出式菜单不显示网页第 2 部分中的导航链接

当您在一个站点上有多个页面并且单击链接只是向下滚动时,如何调用页面?

带有固定标题的JQuery页面滚动问题

extjs 4.1 页面在行更新时滚动到 Internet Explorer 中的网格顶部