A DOM event is a notification generated by the browser when the user or the page performs an action.
In Blazor we can respond to clicks, keys, or form changes with C# methods bound from Razor markup, without manually registering a listener with addEventListener.
Basic @onevent Syntax
The general rule in Blazor is very simple: any standard HTML DOM event has an equivalent Razor syntax that starts with @on followed by the event name.
onclick=>@onclickonchange=>@onchangeoninput=>@oninputonkeydown=>@onkeydown
To handle the event, we simply assign a C# method to that attribute.
<button class="btn btn-primary" @onclick="ProcesarClick">
Click Here
</button>
<p>Message: @mensaje</p>
@code {
private string mensaje = "Waiting...";
private void ProcesarClick()
{
mensaje = "Button pressed at " + DateTime.Now.ToLongTimeString() + "!";
}
}When the event finishes executing, Blazor automatically calls StateHasChanged(). This causes the component to re-render to reflect changes in the UI (like updating the mensaje variable).
Event Arguments
Often, simply knowing that “something happened” is not enough. We need details.
- If it’s a click, where did the user click? Was the CTRL key pressed?
- If it’s a key, which letter was pressed?
Blazor allows us to receive an argument object in our handler method. The type of this object depends on the event.
MouseEventArgs
For mouse events (@onclick, @onmousemove, etc.), the method can accept a MouseEventArgs parameter.
<div style="background-color: lightgray; height: 200px;"
@onclick="MostrarCoordenadas">
Click inside this box.
</div>
<p>@coordenadas</p>
@code {
private string coordenadas;
private void MostrarCoordenadas(MouseEventArgs e)
{
coordenadas = $"X: {e.ClientX}, Y: {e.ClientY}";
if (e.CtrlKey)
{
coordenadas += " (With CTRL key pressed!)";
}
}
}KeyboardEventArgs
For keyboard events, we use KeyboardEventArgs. This is essential for creating keyboard shortcuts or interactive inputs.
<input type="text" @onkeydown="VerificarTecla" placeholder="Type something..." />
<p>@estado</p>
@code {
private string estado;
private void VerificarTecla(KeyboardEventArgs e)
{
if (e.Key == "Enter")
{
estado = "You pressed ENTER! Processing...";
}
else
{
estado = $"Key pressed: {e.Key} (Code: {e.Code})";
}
}
}In a text field, @onchange fires when the user confirms the change (usually when losing focus or pressing Enter), while @oninput responds to every change in value. For real-time search, @oninput is usually the right choice.
Passing Custom Arguments with Lambda Expressions
This is the most common scenario and where most people get stuck at first.
Suppose you have a list generated with @foreach and you want the “Delete” button to act on a specific item.
The standard event handler doesn’t work because we need to pass it the Id or the Item object. The solution is to use a Lambda Expression.
<h3>Task List</h3>
<ul>
@foreach (var tarea in Tareas)
{
<li>
@tarea.Nombre
<button @onclick="() => BorrarTarea(tarea.Id)">
🗑️ Delete
</button>
</li>
}
</ul>
@code {
private void BorrarTarea(int idTarea)
{
var tareaABorrar = Tareas.FirstOrDefault(t => t.Id == idTarea);
if (tareaABorrar != null)
{
Tareas.Remove(tareaABorrar);
}
}
// ... definition of Tareas list ...
}The syntax () => Metodo(parametro) creates a small anonymous function that Blazor will execute when the event occurs.
Event Modifiers: preventDefault and stopPropagation
Sometimes we need to control the native behavior of the browser.
preventDefault: Prevents the default action. For example, preventing a form from submitting or preventing a key press in an input from typing a character.stopPropagation: Prevents the event from “bubbling up” to parent elements.
In Blazor, this is done using attribute directives.
<a href="https://google.com"
@onclick="LogClick"
@onclick:preventDefault="true">
Disabled link
</a>
<div @onclick="ClickPadre">
Parent
<button @onclick="ClickHijo" @onclick:stopPropagation="true">
Child (Does not trigger the parent event)
</button>
</div>Asynchronous Events
Event handlers can return Task. If you are going to wait for a database or an API when a click happens, use async Task instead of async void.
private async Task CargarDatos()
{
IsLoading = true;
// Blazor detects this await and allows the UI to update to show "Loading..."
await Task.Delay(2000);
IsLoading = false;
}Blazor is smart enough to wait for the Task to finish and perform a final render.