路由到 Blazor 中的命名元素(使用锚点导航到特定元素)

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

我无法使用 HTML 锚点导航到 Blazor 服务器中页面的特定 HTML 元素。例如:

@page "/test"

<nav>
    <!-- One version I've tried -->
    <a href="#section2">Section2</a>

    <!-- Another version I've tried -->
    <NavLink href="#section2">Section2</NavLink>    
</nav>

@* ... *@


<h2 id="section2">It's Section2.</h2>
@* ... *@

当我单击指向

Section2
的链接时,我会被重定向到路线 http://localhost:5000/test#section2,但是,它将位于页面顶部。在我看来,浏览器应该向下滚动到由 Element Selector 指定的正确元素,但它不能。

是否必须在 Blazor 中以特殊方式完成?

我在 .Net6 中使用 Blazor 6 和 Visual Studio 2022(版本:17.0.2)。

html navigation blazor anchor blazor-server-side
3个回答
2
投票

您还可以使用使用内置 Blazor JS 的

ElementReference
FocusAsync
。要使用它,您需要使用一个小技巧来使组件“可聚焦”,即设置一个
tabindex
。我使用了
span
,但你可以使用你喜欢的。我使用 @alessandromanzini 的代码从
NavigationManager
获取元素。

这是一个组件:

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.Routing;
using System.Diagnostics.CodeAnalysis;

namespace SO75358165;

public class Bookmark : ComponentBase, IDisposable
{
    private bool _setFocus;

    [Inject] private NavigationManager NavManager { get; set; } = default!;
    [Parameter] public RenderFragment? ChildContent { get; set; }
    [Parameter] public string? BookmarkName { get; set; }
    [DisallowNull] public ElementReference? Element { get; private set; }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        builder.OpenElement(0, "span");
        builder.AddAttribute(2, "tabindex", "-1");
        builder.AddContent(3, this.ChildContent);
        builder.AddElementReferenceCapture(4, this.SetReference);
        builder.CloseElement();
    }

    protected override void OnInitialized()
        => NavManager.LocationChanged += this.OnLocationChanged;

    protected override void OnParametersSet()
        => _setFocus = this.IsMe();

    private void SetReference(ElementReference reference)
        => this.Element = reference;

    private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
    {
        if (this.IsMe())
        {
            _setFocus = true;
            this.StateHasChanged();
        }
    }

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
        if (_setFocus)
            await this.Element!.Value.FocusAsync(false);

        _setFocus = false;
    }

    private bool IsMe()
    {
        string? elementId = null;

        var uri = new Uri(this.NavManager.Uri, UriKind.Absolute);
        if (uri.Fragment.StartsWith('#'))
        {
            elementId = uri.Fragment.Substring(1);
            return elementId == BookmarkName;
        }
        return false;
    }

    public void Dispose()
        => NavManager.LocationChanged -= this.OnLocationChanged;
}

这是我的测试页面:

@page "/"
<PageTitle>Index</PageTitle>
<NavLink href="#me">To me</NavLink>
<h1>Hello, world!</h1>
<h1>Hello, world!</h1>
<h1>Hello, world!</h1>
//.....
<h1>Hello, world!</h1>
<Bookmark BookmarkName="me" >
    <h1 id="me">Focus on Me</h1>
</Bookmark>

2
投票

加载页面后,浏览器会自动滚动到 URL 片段部分中由其 id 标识的元素。当您单击具有 #element-id 类型的 href 的锚点时,它会执行相同的操作。

页面加载行为不适用于 Blazor 服务器,因为页面加载时该元素尚不存在。

解决方案是使用 javascript 和 razor 组件手动创建滚动条:

首先,创建一个像这样的剃刀组件

@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager
@implements IDisposable
@code {
    protected override void OnInitialized()
    {
        NavigationManager.LocationChanged += OnLocationChanged;
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await ScrollToFragment();
    }

    public void Dispose()
    {
        NavigationManager.LocationChanged -= OnLocationChanged;
    }

    private async void OnLocationChanged(object sender, LocationChangedEventArgs e)
    {
        await ScrollToFragment();
    }

    private async Task ScrollToFragment()
    {
        var uri = new Uri(NavigationManager.Uri, UriKind.Absolute);
        var fragment = uri.Fragment;
        if (fragment.StartsWith('#'))
        {
            // Handle text fragment (https://example.org/#test:~:text=foo)
            // https://github.com/WICG/scroll-to-text-fragment/
            var elementId = fragment.Substring(1);
            var index = elementId.IndexOf(":~:", StringComparison.Ordinal);
            if (index > 0)
            {
                elementId = elementId.Substring(0, index);
            }

            if (!string.IsNullOrEmpty(elementId))
            {
                await JSRuntime.InvokeVoidAsync("BlazorScrollToId", elementId);
            }
        }
    }
}

然后在 Blazor 脚本呈现之前的某个位置添加此 javascript 代码。你可以用脚本标签包裹它并将其放在头部。

function BlazorScrollToId(id) {
            const element = document.getElementById(id);
            if (element instanceof HTMLElement) {
                element.scrollIntoView({
                    behavior: "smooth",
                    block: "start",
                    inline: "nearest"
                });
            }
        }

如果需要,最后在您的页面中实现它。您还可以将其放置在布局中,这样它将适用于您创建的每个页面。

@page "/"

<PageTitle>Index</PageTitle>

<a href="#my-id">
    <h1>Hello, world!</h1>
</a>

<SurveyPrompt Title="How is Blazor working for you?" />

<div style="height: 2000px">

</div>

<div id="my-id">
    Hello!
</div>

<AnchorNavigation />

来源:链接


0
投票

看到这个问题中的其他可怕的解决方法后,我决定尝试一个简单的解决方法:

查找

 href="#(.*?)"
替换为
 onclick="window.location.hash = '$1'"

您可能需要在 style.css 中添加一些额外的样式来处理没有 href 的锚点,但我认为除了完全创建另一个组件之外,这是一个简单/可接受的解决方法。

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