Reputation: 333
I'm new to Blazor and I've created a pretty simple Webassembly app. I want a href link to go to a div lower down the page when I click on it, but the Javascript click event won't work. In the Index.razor page the JsRuntime.InvokeVoidAsync("clicker")
is working and the alert("In clicker")
happens as the page loads, but the click / href to go the "intro" div will not work for love nor money :-/
index.html
<!DOCTYPE html>
<html>
<head>
<title>My Blazor App</title>
<!--script type='text/javascript' src='./scripts/app.js'-->
</head>
<body>
<app>Loading...</app>
<script src="_framework/blazor.webassembly.js"></script>
<script>
function clicker() {
alert("In clicker"); // this works
document.getElementById('skip').onclick = function(e){
alert("clicked"); // this works but the page still won't scroll to the "intro" div :(
}
}
//clicker();
</script>
</body>
</html>
Index.razor (@code section is at top of page)
@page "/"
@code {
[Inject]
protected IJSRuntime JsRuntime { get; set; }
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
JsRuntime.InvokeVoidAsync("clicker");
}
}
}
// This link won't go to the intro div when clicked :(
<a id="skip" class="skip" href="#intro">skip this bit</a>
...
<div id="intro" class="home">
...
</div>
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IComponentsApplicationBuilder app)
{
app.AddComponent<App>("app");
}
}
If anyone could shed some light on this it would save my week.
Upvotes: 1
Views: 3405
Reputation: 20141
The page won’t scroll to the element you’ve specified in the link.This has to do with how routing is handled in Blazor and most other SPA applications as well. A simple solution is that you could create your own AnchorLink component and use a little bit of JavaScript interop magic.
1.Create AnchorLink.razor
in Pages/Shared
@code {
public AnchorLink()
{
this.Attributes = new Dictionary<string, object>();
}
private string targetId = null;
private bool preventDefault = false;
/// <summary>
/// This parameter supports arbitrary attributes.
/// </summary>
/// <remarks>
/// Any attribute specified on the component, which is not defined as a parameter, whill be added to this dictionary.
/// It is then uses as the source for attributes rendered onto the resulting HTML element below in the markup section
/// of this component.
/// For details, refer to <see cref="https://learn.microsoft.com/en-us/aspnet/core/blazor/components#attribute-splatting-and-arbitrary-parameters"/>.
/// </remarks>
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> Attributes { get; set; }
/// <summary>
/// Supports child content for the component.
/// </summary>
/// <see cref="https://learn.microsoft.com/en-us/aspnet/core/blazor/components#child-content"/>
[Parameter]
public RenderFragment ChildContent { get; set; }
[Inject]
protected IJSRuntime JsInterop { get; set; }
protected override void OnParametersSet()
{
string href = null;
if (this.Attributes.ContainsKey("href"))
{
// If the href attribute has been specified, we examine the value of it. If if starts with '#'
// we assume the rest of the value contains the ID of the element the link points to.
href = $"{this.Attributes["href"]}";
if (href.StartsWith("#"))
{
// If the href contains an anchor link we don't want the default click action to occur, but
// rather take care of the click in our own method.
this.targetId = href.Substring(1);
this.preventDefault = true;
}
}
base.OnParametersSet();
}
private async Task AnchorOnClickAsync()
{
if (!string.IsNullOrEmpty(this.targetId))
{
// If the target ID has been specified, we know this is an anchor link that we need to scroll
// to, so we call the JavaScript method to take care of this for us.
await this.JsInterop.InvokeVoidAsync("anchorLink.scrollIntoView", this.targetId);
}
}
}
<a href="" @onclick="this.AnchorOnClickAsync" @onclick:stopPropagation="false" />
<a @attributes="this.Attributes" @onclick="this.AnchorOnClickAsync" @onclick:preventDefault="this.preventDefault">Hello @this.ChildContent</a>
2.Add js in wwwroot/Index.html
<script src="_framework/blazor.webassembly.js"></script>
<script>
window.anchorLink = {
scrollIntoView: function (elementId) {
// This function is called from the AnchorLink component using JavaScript interop.
// It will try to find an element using the ID given to the function, and scroll that
// element into view, if an element is found.
var elem = document.getElementById(elementId);
if (elem) {
elem.scrollIntoView();
window.location.hash = elementId;
}
}
}
</script>
3.Index.razor
<AnchorLink class="skip" href="#intro">skip this bit</AnchorLink>
Refer to https://mikaberglund.com/2019/12/28/creating-anchor-links-in-blazor-applications/
Upvotes: 2
Reputation: 9029
There is no need for JavaScript here.
If you add a specific target to your markup, it will just work.
You can use target="_top" to avoid Blazor navigation interception.
<a class="skip" href="#intro" target="_top">skip this bit</a>
...
<div id="intro" class="home">
...
</div>
Note that target="_top"
just directs the browser to navigate within the topmost frame in the window, it does not mean you will scroll to the top!
Upvotes: 6