我无法使用 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)。
您还可以使用使用内置 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>
加载页面后,浏览器会自动滚动到 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 />
来源:链接
看到这个问题中的其他可怕的解决方法后,我决定尝试一个简单的解决方法:
查找
href="#(.*?)"
替换为 onclick="window.location.hash = '$1'"
您可能需要在 style.css 中添加一些额外的样式来处理没有 href 的锚点,但我认为除了完全创建另一个组件之外,这是一个简单/可接受的解决方法。