Blazor Components - Free Anchor Navigation Tool

ASP.NET Team Blog
28 August 2019

As you may know, Blazor does not currently support navigation via anchors. In addition, Blazor does not support hyperlinks that only include anchor IDs within the href attribute (<a href=”#MyAnchor1”>some text</a>). To help address these limitations, we created a free navigation tool for both client-side and server-side Blazor projects.

Features and Capabilities

The DevExpress Anchor Navigation tool automatically scrolls a page to an anchor in the following instances:

Our tool also includes a Blazor AnchorLink component. Use this component to create in-page navigation links as needed:

<AnchorLink class="nav-link py-3 px-4" href="#MySection1">My Section 1</AnchorLink>
<AnchorLink class="nav-link py-3 px-4" href="#MySection2">My Section 2</AnchorLink>

When an end-user clicks the link, the page scrolls to the corresponding anchor:

<h2 id="MySection1">Section 1</h2>
<p>Lorem ipsum dolor sit amet...</p>
<h2 id="MySection2">Section 2</h2>
<p>Quisque imperdiet risus quis nisl vulputate...</p>

Integrating the Anchor Navigation Tool

Full source code for our Blazor Anchor Navigation tool is available on GitHub. Please follow the instructions below to include it within your next Blazor project.

  1. Download the DevExpress.Blazor.AnchorUtils project and add it to your Blazor solution.
  2. Add the DevExpress.Blazor.AnchorUtils namespace to _Imports.razor:

@using DevExpress.Blazor.AnchorUtils

  1. Add AnchorUtilsComponent to Shared/MainLayout.razor file:
<div>
    ...
    <div class="content px-4">
          @Body
    </div>
</div>
<AnchorUtilsComponent />
  1. Find the JavaScript wwwroot/anchor-utils.js file in the anchor navigation source code. Copy this file to the wwwroot folder or to its subfolders.
  • For server-side Blazor: register this file in Pages/_Host.cshtml of your project.
  • For client-side Blazor: register this file in wwwroot/index.html of your project.

<script type="text/javascript" src="~/anchor-utils.js"></script>

  1. (optional) If your page layout contains a pinned (non-scrollable) header (like the standard Blazor project), edit the anchor-utils.js file and update the following code to obtain vertical scroll offset appropriate for your application:

y -= document.querySelector(".main .top-row").offsetHeight;

Note: If a client-side Blazor application is deployed as a set of static files, the web server provides the index.html file by a root URL (e.g., https://mywebsite.com/) only. If a browser requests a specific page by its direct URL (e.g., https://mywebsite.com/MyPage1), the web server raises a 404-code exception. Refer to the Getting 404 on pages other than root when Blazor hosted in IIS to learn more about this issue and a possible solution. The solution requires URL Rewrite Module.

How it works

First, we need to obtain an anchor from the current URI (if it exists). We’ll use the IUriHelper OnLocationChanged event event to do so:

@inject IUriHelper UriHelper
...
@code {
    string Anchor { get; set; }
    bool ForceScroll { get; set; }
 
    protected override void OnInitialized()
    {
        base.OnInitialized();
        ForceScroll = true;
        UriHelper.OnLocationChanged += OnLocationChanged;
    }
 
    void OnLocationChanged(object sender, LocationChangedEventArgs args)
    {
        var anchor = UriHelper.ToAbsoluteUri(args.Location).Fragment;
        ...
    } 
}

The ScrollToAnchor C# method invokes the scrollToAnchor JavaScript function and sends an anchor ID as a parameter:

...
@inject IJSRuntime JSRuntime
@inject IComponentContext ComponentContext
...
    bool ScrollToAnchor(string anchor)
    {
        if (ComponentContext.IsConnected && (!string.IsNullOrEmpty(anchor) || ForceScroll))
        {
            JSRuntime.InvokeAsync<string>("scrollToAnchor", anchor);
            return true;
        }
        return false;
    }

We’ll call the ScrollToAnchor method in the OnLocationChanged event handler to scroll the page when navigation location changes. We’ll also call the method in the OnAfterRenderAsync event handler to initiate scrolling (if necessary) when a page first loads:

protected override Task OnAfterRenderAsync()
    {
        ScrollToAnchor(Anchor);
        if (ForceScroll)
        {
            ForceScroll = false;
        }
        return base.OnAfterRenderAsync();
    }
 
    void OnLocationChanged(object sender, LocationChangedEventArgs args)
    {
        var anchor = UriHelper.ToAbsoluteUri(args.Location).Fragment;
        if (!ScrollToAnchor(anchor))
        {
            Anchor = anchor;
        }
    }

The scrollToAnchor JavaScript function scrolls to the required anchor element.

Javascript:

function scrollToAnchor(anchor) {
    var selector = anchor || document.location.hash;
    if (selector && selector.length > 1)
    {
        var element = document.querySelector(selector);
        if (element)
        {
            var y = element.getBoundingClientRect().top + window.pageYOffset;
            /* The following code updates the vertical scroll bar's offset for a standard Blazor Visual Studio template.
               Update the code to get an offset that is suitable for your application.  */
            y -= document.querySelector(".main .top-row").offsetHeight
            window.scroll(0, y);
        }
    }
    else
    {
        window.scroll(0, 0);
    }
}

Feedback

As always, your feedback matters. Do you plan on using anchors in your Blazor applications? Post your comments below and share your Blazor-related development experiences with us.

no comments
No Comments

Please login or register to post comments.