ASP.NET Core Blazor event handling
Note
This isn't the latest version of this article. To switch to the latest, use the ASP.NET Core version selector at the top of the table of contents.
If the selector isn't visible in a narrow browser window, widen the window or select the vertical ellipsis (⋮) > Table of contents.
This article explains Blazor's event handling features, including event argument types, event callbacks, and managing default browser events.
Specify delegate event handlers in Razor component markup with @on{DOM EVENT}="{DELEGATE}"
Razor syntax:
- The
{DOM EVENT}
placeholder is a Document Object Model (DOM) event (for example,click
). - The
{DELEGATE}
placeholder is the C# delegate event handler.
For event handling:
- Asynchronous delegate event handlers that return a Task are supported.
- Delegate event handlers automatically trigger a UI render, so there's no need to manually call
StateHasChanged
. - Exceptions are logged.
The following code:
- Calls the
UpdateHeading
method when the button is selected in the UI. - Calls the
CheckChanged
method when the checkbox is changed in the UI.
Pages/EventHandlerExample1.razor
:
@page "/event-handler-example-1"
<h1>@currentHeading</h1>
<p>
<label>
New title
<input @bind="newHeading" />
</label>
<button @onclick="UpdateHeading">
Update heading
</button>
</p>
<p>
<label>
<input type="checkbox" @onchange="CheckChanged" />
@checkedMessage
</label>
</p>
@code {
private string currentHeading = "Initial heading";
private string? newHeading;
private string checkedMessage = "Not changed yet";
private void UpdateHeading()
{
currentHeading = $"{newHeading}!!!";
}
private void CheckChanged()
{
checkedMessage = $"Last changed at {DateTime.Now}";
}
}
In the following example, UpdateHeading
:
- Is called asynchronously when the button is selected.
- Waits two seconds before updating the heading.
Pages/EventHandlerExample2.razor
:
@page "/event-handler-example-2"
<h1>@currentHeading</h1>
<p>
<label>
New title
<input @bind="newHeading" />
</label>
<button @onclick="UpdateHeading">
Update heading
</button>
</p>
@code {
private string currentHeading = "Initial heading";
private string? newHeading;
private async Task UpdateHeading()
{
await Task.Delay(2000);
currentHeading = $"{newHeading}!!!";
}
}
Event arguments
Built-in event arguments
For events that support an event argument type, specifying an event parameter in the event method definition is only necessary if the event type is used in the method. In the following example, MouseEventArgs is used in the ReportPointerLocation
method to set message text that reports the mouse coordinates when the user selects a button in the UI.
Pages/EventHandlerExample3.razor
:
@page "/event-handler-example-3"
@for (var i = 0; i < 4; i++)
{
<p>
<button @onclick="ReportPointerLocation">
Where's my mouse pointer for this button?
</button>
</p>
}
<p>@mousePointerMessage</p>
@code {
private string? mousePointerMessage;
private void ReportPointerLocation(MouseEventArgs e)
{
mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
}
}
Supported EventArgs are shown in the following table.
Event | Class | Document Object Model (DOM) notes |
---|---|---|
Clipboard | ClipboardEventArgs | |
Drag | DragEventArgs | DataTransfer and DataTransferItem hold dragged item data. Implement drag and drop in Blazor apps using JS interop with HTML Drag and Drop API. |
Error | ErrorEventArgs | |
Event | EventArgs | EventHandlers holds attributes to configure the mappings between event names and event argument types. |
Focus | FocusEventArgs | Doesn't include support for relatedTarget . |
Input | ChangeEventArgs | |
Keyboard | KeyboardEventArgs | |
Mouse | MouseEventArgs | |
Mouse pointer | PointerEventArgs | |
Mouse wheel | WheelEventArgs | |
Progress | ProgressEventArgs | |
Touch | TouchEventArgs | TouchPoint represents a single contact point on a touch-sensitive device. |
For more information, see the following resources:
EventArgs
classes in the ASP.NET Core reference source (dotnet/aspnetcoremain
branch)Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
EventHandlers holds attributes to configure the mappings between event names and event argument types.
Custom event arguments
Blazor supports custom event arguments, which enable you to pass arbitrary data to .NET event handlers with custom events.
General configuration
Custom events with custom event arguments are generally enabled with the following steps.
In JavaScript, define a function for building the custom event argument object from the source event:
function eventArgsCreator(event) { return { customProperty1: 'any value for property 1', customProperty2: event.srcElement.value }; }
Register the custom event with the preceding handler in a JavaScript initializer.
wwwroot/{PACKAGE ID/ASSEMBLY NAME}.lib.module.js
:export function afterStarted(blazor) { blazor.registerCustomEventType('customevent', { createEventArgs: eventArgsCreator }); }
In the preceding example, the
{PACKAGE ID/ASSEMBLY NAME}
placeholder of the file name represents the package ID or assembly name of the app.Note
The call to
registerCustomEventType
is performed in a script only once per event.For the call to
registerCustomEventType
, use theblazor
parameter (lowercaseb
) provided byafterStarted
. Although the registration is valid when using theBlazor
object (uppercaseB
), the preferred approach is to use the parameter.Define a class for the event arguments:
namespace BlazorSample.CustomEvents; public class CustomEventArgs : EventArgs { public string? CustomProperty1 {get; set;} public string? CustomProperty2 {get; set;} }
Wire up the custom event with the event arguments by adding an EventHandlerAttribute attribute annotation for the custom event:
- In order for the compiler to find the
[EventHandler]
class, it must be placed into a C# class file (.cs
), making it a normal top-level class. - Mark the class
public
. - The class doesn't require members.
- The class must be called "
EventHandlers
" in order to be found by the Razor compiler. - Place the class under a namespace specific to your app.
- Import the namespace into the Razor component (
.razor
) where the event is used.
using Microsoft.AspNetCore.Components; namespace BlazorSample.CustomEvents; [EventHandler("oncustomevent", typeof(CustomEventArgs), enableStopPropagation: true, enablePreventDefault: true)] public static class EventHandlers { }
- In order for the compiler to find the
Register the event handler on one or more HTML elements. Access the data that was passed in from JavaScript in the delegate handler method:
@using namespace BlazorSample.CustomEvents <button @oncustomevent="HandleCustomEvent">Handle</button> @code { private void HandleCustomEvent(CustomEventArgs eventArgs) { // eventArgs.CustomProperty1 // eventArgs.CustomProperty2 } }
If the @oncustomevent
attribute isn't recognized by IntelliSense, make sure that the component or the _Imports.razor
file contains an @using
statement for the namespace containing the EventHandler
class.
Whenever the custom event is fired on the DOM, the event handler is called with the data passed from the JavaScript.
If you're attempting to fire a custom event, bubbles
must be enabled by setting its value to true
. Otherwise, the event doesn't reach the Blazor handler for processing into the C# custom EventHandlerAttribute method. For more information, see MDN Web Docs: Event bubbling.
Custom clipboard paste event example
The following example receives a custom clipboard paste event that includes the time of the paste and the user's pasted text.
Declare a custom name (oncustompaste
) for the event and a .NET class (CustomPasteEventArgs
) to hold the event arguments for this event:
CustomEvents.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.CustomEvents;
[EventHandler("oncustompaste", typeof(CustomPasteEventArgs),
enableStopPropagation: true, enablePreventDefault: true)]
public static class EventHandlers
{
}
public class CustomPasteEventArgs : EventArgs
{
public DateTime EventTimestamp { get; set; }
public string? PastedData { get; set; }
}
Add JavaScript code to supply data for the EventArgs subclass with the preceding handler in a JavaScript initializer. The following example only handles pasting text, but you could use arbitrary JavaScript APIs to deal with users pasting other types of data, such as images.
wwwroot/{PACKAGE ID/ASSEMBLY NAME}.lib.module.js
:
export function afterStarted(blazor) {
blazor.registerCustomEventType('custompaste', {
browserEventName: 'paste',
createEventArgs: event => {
return {
eventTimestamp: new Date(),
pastedData: event.clipboardData.getData('text')
};
}
});
}
In the preceding example, the {PACKAGE ID/ASSEMBLY NAME}
placeholder of the file name represents the package ID or assembly name of the app.
Note
For the call to registerCustomEventType
, use the blazor
parameter (lowercase b
) provided by afterStarted
. Although the registration is valid when using the Blazor
object (uppercase B
), the preferred approach is to use the parameter.
The preceding code tells the browser that when a native paste
event occurs:
- Raise a
custompaste
event. - Supply the event arguments data using the custom logic stated:
- For the
eventTimestamp
, create a new date. - For the
pastedData
, get the clipboard data as text. For more information, see MDN Web Docs: ClipboardEvent.clipboardData.
- For the
Event name conventions differ between .NET and JavaScript:
- In .NET, event names are prefixed with "
on
". - In JavaScript, event names don't have a prefix.
In a Razor component, attach the custom handler to an element.
Pages/CustomPasteArguments.razor
:
@page "/custom-paste-arguments"
@using BlazorSample.CustomEvents
<label>
Try pasting into the following text box:
<input @oncustompaste="HandleCustomPaste" />
</label>
<p>
@message
</p>
@code {
private string? message;
private void HandleCustomPaste(CustomPasteEventArgs eventArgs)
{
message = $"At {eventArgs.EventTimestamp.ToShortTimeString()}, " +
$"you pasted: {eventArgs.PastedData}";
}
}
Lambda expressions
Lambda expressions are supported as the delegate event handler.
Pages/EventHandlerExample4.razor
:
@page "/event-handler-example-4"
<h1>@heading</h1>
<p>
<button @onclick="@(e => heading = "New heading!!!")">
Update heading
</button>
</p>
@code {
private string heading = "Initial heading";
}
It's often convenient to close over additional values using C# method parameters, such as when iterating over a set of elements. The following example creates three buttons, each of which calls UpdateHeading
and passes the following data:
- An event argument (MouseEventArgs) in
e
. - The button number in
buttonNumber
.
Pages/EventHandlerExample5.razor
:
@page "/event-handler-example-5"
<h1>@heading</h1>
@for (var i = 1; i < 4; i++)
{
var buttonNumber = i;
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@i
</button>
</p>
}
@code {
private string heading = "Select a button to learn its position";
private void UpdateHeading(MouseEventArgs e, int buttonNumber)
{
heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
}
}
Creating a large number of event delegates in a loop may cause poor rendering performance. For more information, see ASP.NET Core Blazor performance best practices.
Avoid using a loop variable directly in a lambda expression, such as i
in the preceding for
loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Capture the variable's value in a local variable. In the preceding example:
- The loop variable
i
is assigned tobuttonNumber
. buttonNumber
is used in the lambda expression.
Alternatively, use a foreach
loop with Enumerable.Range, which doesn't suffer from the preceding problem:
@foreach (var buttonNumber in Enumerable.Range(1,3))
{
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@buttonNumber
</button>
</p>
}
EventCallback
A common scenario with nested components executes a parent component's method when a child component event occurs. An onclick
event occurring in the child component is a common use case. To expose events across components, use an EventCallback. A parent component can assign a callback method to a child component's EventCallback.
The following Child
component demonstrates how a button's onclick
handler is set up to receive an EventCallback delegate from the sample's ParentComponent
. The EventCallback is typed with MouseEventArgs
, which is appropriate for an onclick
event from a peripheral device.
Shared/Child.razor
:
<p>
<button @onclick="OnClickCallback">
Trigger a Parent component method
</button>
</p>
@code {
[Parameter]
public string? Title { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
The Parent
component sets the child's EventCallback<TValue> (OnClickCallback
) to its ShowMessage
method.
Pages/Parent.razor
:
@page "/parent"
<h1>Parent-child example</h1>
<Child Title="Panel Title from Parent" OnClickCallback="@ShowMessage">
Content of the child component is supplied by the parent component.
</Child>
<p>@message</p>
@code {
private string? message;
private void ShowMessage(MouseEventArgs e)
{
message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
}
}
When the button is selected in the ChildComponent
:
- The
Parent
component'sShowMessage
method is called.message
is updated and displayed in theParent
component. - A call to
StateHasChanged
isn't required in the callback's method (ShowMessage
). StateHasChanged is called automatically to rerender theParent
component, just as child events trigger component rerendering in event handlers that execute within the child. For more information, see ASP.NET Core Razor component rendering.
EventCallback and EventCallback<TValue> permit asynchronous delegates. EventCallback is weakly typed and allows passing any type argument in InvokeAsync(Object)
. EventCallback<TValue> is strongly typed and requires passing a T
argument in InvokeAsync(T)
that's assignable to TValue
.
<ChildComponent
OnClickCallback="@(async () => { await Task.Yield(); messageText = "Blaze It!"; })" />
Invoke an EventCallback or EventCallback<TValue> with InvokeAsync and await the Task:
await OnClickCallback.InvokeAsync(arg);
Use EventCallback and EventCallback<TValue> for event handling and binding component parameters.
Prefer the strongly typed EventCallback<TValue> over EventCallback. EventCallback<TValue> provides enhanced error feedback to users of the component. Similar to other UI event handlers, specifying the event parameter is optional. Use EventCallback when there's no value passed to the callback.
Prevent default actions
Use the @on{DOM EVENT}:preventDefault
directive attribute to prevent the default action for an event, where the {DOM EVENT}
placeholder is a Document Object Model (DOM) event.
When a key is selected on an input device and the element focus is on a text box, a browser normally displays the key's character in the text box. In the following example, the default behavior is prevented by specifying the @onkeydown:preventDefault
directive attribute. When the focus is on the <input>
element, the counter increments with the key sequence Shift++. The +
character isn't assigned to the <input>
element's value. For more information on keydown
, see MDN Web Docs: Document: keydown
event.
Pages/EventHandlerExample6.razor
:
@page "/event-handler-example-6"
<p>
<input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
</p>
@code {
private int count = 0;
private void KeyHandler(KeyboardEventArgs e)
{
if (e.Key == "+")
{
count++;
}
}
}
Specifying the @on{DOM EVENT}:preventDefault
attribute without a value is equivalent to @on{DOM EVENT}:preventDefault="true"
.
An expression is also a permitted value of the attribute. In the following example, shouldPreventDefault
is a bool
field set to either true
or false
:
<input @onkeydown:preventDefault="shouldPreventDefault" />
...
@code {
private bool shouldPreventDefault = true;
}
Stop event propagation
Use the @on{DOM EVENT}:stopPropagation
directive attribute to stop event propagation within the Blazor scope. {DOM EVENT}
is a placeholder for a Document Object Model (DOM) event.
The stopPropagation
directive attribute's effect is limited to the Blazor scope and doesn't extend to the HTML DOM. Events must propagate to the HTML DOM root before Blazor can act upon them. For a mechanism to prevent HTML DOM event propagation, consider the following approach:
- Obtain the event's path by calling
Event.composedPath()
. - Filter events based on the composed event targets (
EventTarget
).
In the following example, selecting the checkbox prevents click events from the second child <div>
from propagating to the parent <div>
. Since propagated click events normally fire the OnSelectParentDiv
method, selecting the second child <div>
results in the parent <div>
message appearing unless the checkbox is selected.
Pages/EventHandlerExample7.razor
:
@page "/event-handler-example-7"
<label>
<input @bind="stopPropagation" type="checkbox" />
Stop Propagation
</label>
<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
<h3>Parent div</h3>
<div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
Child div that doesn't stop propagation when selected.
</div>
<div class="m-1 p-1 border" @onclick="OnSelectChildDiv"
@onclick:stopPropagation="stopPropagation">
Child div that stops propagation when selected.
</div>
</div>
<p>
@message
</p>
@code {
private bool stopPropagation = false;
private string? message;
private void OnSelectParentDiv() =>
message = $"The parent div was selected. {DateTime.Now}";
private void OnSelectChildDiv() =>
message = $"A child div was selected. {DateTime.Now}";
}
Focus an element
Call FocusAsync on an element reference to focus an element in code. In the following example, select the button to focus the <input>
element.
Pages/EventHandlerExample8.razor
:
@page "/event-handler-example-8"
<p>
<input @ref="exampleInput" />
</p>
<button @onclick="ChangeFocus">
Focus the Input Element
</button>
@code {
private ElementReference exampleInput;
private async Task ChangeFocus()
{
await exampleInput.FocusAsync();
}
}
Specify delegate event handlers in Razor component markup with @on{DOM EVENT}="{DELEGATE}"
Razor syntax:
- The
{DOM EVENT}
placeholder is a Document Object Model (DOM) event (for example,click
). - The
{DELEGATE}
placeholder is the C# delegate event handler.
For event handling:
- Asynchronous delegate event handlers that return a Task are supported.
- Delegate event handlers automatically trigger a UI render, so there's no need to manually call
StateHasChanged
. - Exceptions are logged.
The following code:
- Calls the
UpdateHeading
method when the button is selected in the UI. - Calls the
CheckChanged
method when the checkbox is changed in the UI.
Pages/EventHandlerExample1.razor
:
@page "/event-handler-example-1"
<h1>@currentHeading</h1>
<p>
<label>
New title
<input @bind="newHeading" />
</label>
<button @onclick="UpdateHeading">
Update heading
</button>
</p>
<p>
<label>
<input type="checkbox" @onchange="CheckChanged" />
@checkedMessage
</label>
</p>
@code {
private string currentHeading = "Initial heading";
private string? newHeading;
private string checkedMessage = "Not changed yet";
private void UpdateHeading()
{
currentHeading = $"{newHeading}!!!";
}
private void CheckChanged()
{
checkedMessage = $"Last changed at {DateTime.Now}";
}
}
In the following example, UpdateHeading
:
- Is called asynchronously when the button is selected.
- Waits two seconds before updating the heading.
Pages/EventHandlerExample2.razor
:
@page "/event-handler-example-2"
<h1>@currentHeading</h1>
<p>
<label>
New title
<input @bind="newHeading" />
</label>
<button @onclick="UpdateHeading">
Update heading
</button>
</p>
@code {
private string currentHeading = "Initial heading";
private string? newHeading;
private async Task UpdateHeading()
{
await Task.Delay(2000);
currentHeading = $"{newHeading}!!!";
}
}
Event arguments
Built-in event arguments
For events that support an event argument type, specifying an event parameter in the event method definition is only necessary if the event type is used in the method. In the following example, MouseEventArgs is used in the ReportPointerLocation
method to set message text that reports the mouse coordinates when the user selects a button in the UI.
Pages/EventHandlerExample3.razor
:
@page "/event-handler-example-3"
@for (var i = 0; i < 4; i++)
{
<p>
<button @onclick="ReportPointerLocation">
Where's my mouse pointer for this button?
</button>
</p>
}
<p>@mousePointerMessage</p>
@code {
private string? mousePointerMessage;
private void ReportPointerLocation(MouseEventArgs e)
{
mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
}
}
Supported EventArgs are shown in the following table.
Event | Class | Document Object Model (DOM) events and notes |
---|---|---|
Clipboard | ClipboardEventArgs | oncut , oncopy , onpaste |
Drag | DragEventArgs | ondrag , ondragstart , ondragenter , ondragleave , ondragover , ondrop , ondragend DataTransfer and DataTransferItem hold dragged item data. Implement drag and drop in Blazor apps using JS interop with HTML Drag and Drop API. |
Error | ErrorEventArgs | onerror |
Event | EventArgs | Generalonactivate , onbeforeactivate , onbeforedeactivate , ondeactivate , onfullscreenchange , onfullscreenerror , onloadeddata , onloadedmetadata , onpointerlockchange , onpointerlockerror , onreadystatechange , onscroll Clipboard onbeforecut , onbeforecopy , onbeforepaste Input oninvalid , onreset , onselect , onselectionchange , onselectstart , onsubmit Media oncanplay , oncanplaythrough , oncuechange , ondurationchange , onemptied , onended , onpause , onplay , onplaying , onratechange , onseeked , onseeking , onstalled , onstop , onsuspend , ontimeupdate , ontoggle , onvolumechange , onwaiting EventHandlers holds attributes to configure the mappings between event names and event argument types. |
Focus | FocusEventArgs | onfocus , onblur , onfocusin , onfocusout Doesn't include support for relatedTarget . |
Input | ChangeEventArgs | onchange , oninput |
Keyboard | KeyboardEventArgs | onkeydown , onkeypress , onkeyup |
Mouse | MouseEventArgs | onclick , oncontextmenu , ondblclick , onmousedown , onmouseup , onmouseover , onmousemove , onmouseout |
Mouse pointer | PointerEventArgs | onpointerdown , onpointerup , onpointercancel , onpointermove , onpointerover , onpointerout , onpointerenter , onpointerleave , ongotpointercapture , onlostpointercapture |
Mouse wheel | WheelEventArgs | onwheel , onmousewheel |
Progress | ProgressEventArgs | onabort , onload , onloadend , onloadstart , onprogress , ontimeout |
Touch | TouchEventArgs | ontouchstart , ontouchend , ontouchmove , ontouchenter , ontouchleave , ontouchcancel TouchPoint represents a single contact point on a touch-sensitive device. |
For more information, see EventArgs
classes in the ASP.NET Core reference source (dotnet/aspnetcore main
branch).
Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
Custom event arguments
Blazor supports custom event arguments, which enable you to pass arbitrary data to .NET event handlers with custom events.
General configuration
Custom events with custom event arguments are generally enabled with the following steps.
In JavaScript, define a function for building the custom event argument object from the source event:
function eventArgsCreator(event) { return { customProperty1: 'any value for property 1', customProperty2: event.srcElement.value }; }
Register the custom event with the preceding handler in a JavaScript initializer.
wwwroot/{PACKAGE ID/ASSEMBLY NAME}.lib.module.js
:export function afterStarted(blazor) { blazor.registerCustomEventType('customevent', { createEventArgs: eventArgsCreator }); }
In the preceding example, the
{PACKAGE ID/ASSEMBLY NAME}
placeholder of the file name represents the package ID or assembly name of the app.Note
The call to
registerCustomEventType
is performed in a script only once per event.For the call to
registerCustomEventType
, use theblazor
parameter (lowercaseb
) provided byafterStarted
. Although the registration is valid when using theBlazor
object (uppercaseB
), the preferred approach is to use the parameter.Define a class for the event arguments:
namespace BlazorSample.CustomEvents; public class CustomEventArgs : EventArgs { public string? CustomProperty1 {get; set;} public string? CustomProperty2 {get; set;} }
Wire up the custom event with the event arguments by adding an EventHandlerAttribute attribute annotation for the custom event:
- In order for the compiler to find the
[EventHandler]
class, it must be placed into a C# class file (.cs
), making it a normal top-level class. - Mark the class
public
. - The class doesn't require members.
- The class must be called "
EventHandlers
" in order to be found by the Razor compiler. - Place the class under a namespace specific to your app.
- Import the namespace into the Razor component (
.razor
) where the event is used.
using Microsoft.AspNetCore.Components; namespace BlazorSample.CustomEvents; [EventHandler("oncustomevent", typeof(CustomEventArgs), enableStopPropagation: true, enablePreventDefault: true)] public static class EventHandlers { }
- In order for the compiler to find the
Register the event handler on one or more HTML elements. Access the data that was passed in from JavaScript in the delegate handler method:
@using namespace BlazorSample.CustomEvents <button @oncustomevent="HandleCustomEvent">Handle</button> @code { private void HandleCustomEvent(CustomEventArgs eventArgs) { // eventArgs.CustomProperty1 // eventArgs.CustomProperty2 } }
If the @oncustomevent
attribute isn't recognized by IntelliSense, make sure that the component or the _Imports.razor
file contains an @using
statement for the namespace containing the EventHandler
class.
Whenever the custom event is fired on the DOM, the event handler is called with the data passed from the JavaScript.
If you're attempting to fire a custom event, bubbles
must be enabled by setting its value to true
. Otherwise, the event doesn't reach the Blazor handler for processing into the C# custom EventHandlerAttribute method. For more information, see MDN Web Docs: Event bubbling.
Custom clipboard paste event example
The following example receives a custom clipboard paste event that includes the time of the paste and the user's pasted text.
Declare a custom name (oncustompaste
) for the event and a .NET class (CustomPasteEventArgs
) to hold the event arguments for this event:
CustomEvents.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.CustomEvents;
[EventHandler("oncustompaste", typeof(CustomPasteEventArgs),
enableStopPropagation: true, enablePreventDefault: true)]
public static class EventHandlers
{
}
public class CustomPasteEventArgs : EventArgs
{
public DateTime EventTimestamp { get; set; }
public string? PastedData { get; set; }
}
Add JavaScript code to supply data for the EventArgs subclass with the preceding handler in a JavaScript initializer. The following example only handles pasting text, but you could use arbitrary JavaScript APIs to deal with users pasting other types of data, such as images.
wwwroot/{PACKAGE ID/ASSEMBLY NAME}.lib.module.js
:
export function afterStarted(blazor) {
blazor.registerCustomEventType('custompaste', {
browserEventName: 'paste',
createEventArgs: event => {
return {
eventTimestamp: new Date(),
pastedData: event.clipboardData.getData('text')
};
}
});
}
In the preceding example, the {PACKAGE ID/ASSEMBLY NAME}
placeholder of the file name represents the package ID or assembly name of the app.
Note
For the call to registerCustomEventType
, use the blazor
parameter (lowercase b
) provided by afterStarted
. Although the registration is valid when using the Blazor
object (uppercase B
), the preferred approach is to use the parameter.
The preceding code tells the browser that when a native paste
event occurs:
- Raise a
custompaste
event. - Supply the event arguments data using the custom logic stated:
- For the
eventTimestamp
, create a new date. - For the
pastedData
, get the clipboard data as text. For more information, see MDN Web Docs: ClipboardEvent.clipboardData.
- For the
Event name conventions differ between .NET and JavaScript:
- In .NET, event names are prefixed with "
on
". - In JavaScript, event names don't have a prefix.
In a Razor component, attach the custom handler to an element.
Pages/CustomPasteArguments.razor
:
@page "/custom-paste-arguments"
@using BlazorSample.CustomEvents
<label>
Try pasting into the following text box:
<input @oncustompaste="HandleCustomPaste" />
</label>
<p>
@message
</p>
@code {
private string? message;
private void HandleCustomPaste(CustomPasteEventArgs eventArgs)
{
message = $"At {eventArgs.EventTimestamp.ToShortTimeString()}, " +
$"you pasted: {eventArgs.PastedData}";
}
}
Lambda expressions
Lambda expressions are supported as the delegate event handler.
Pages/EventHandlerExample4.razor
:
@page "/event-handler-example-4"
<h1>@heading</h1>
<p>
<button @onclick="@(e => heading = "New heading!!!")">
Update heading
</button>
</p>
@code {
private string heading = "Initial heading";
}
It's often convenient to close over additional values using C# method parameters, such as when iterating over a set of elements. The following example creates three buttons, each of which calls UpdateHeading
and passes the following data:
- An event argument (MouseEventArgs) in
e
. - The button number in
buttonNumber
.
Pages/EventHandlerExample5.razor
:
@page "/event-handler-example-5"
<h1>@heading</h1>
@for (var i = 1; i < 4; i++)
{
var buttonNumber = i;
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@i
</button>
</p>
}
@code {
private string heading = "Select a button to learn its position";
private void UpdateHeading(MouseEventArgs e, int buttonNumber)
{
heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
}
}
Creating a large number of event delegates in a loop may cause poor rendering performance. For more information, see ASP.NET Core Blazor performance best practices.
Avoid using a loop variable directly in a lambda expression, such as i
in the preceding for
loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Capture the variable's value in a local variable. In the preceding example:
- The loop variable
i
is assigned tobuttonNumber
. buttonNumber
is used in the lambda expression.
Alternatively, use a foreach
loop with Enumerable.Range, which doesn't suffer from the preceding problem:
@foreach (var buttonNumber in Enumerable.Range(1,3))
{
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@buttonNumber
</button>
</p>
}
EventCallback
A common scenario with nested components executes a parent component's method when a child component event occurs. An onclick
event occurring in the child component is a common use case. To expose events across components, use an EventCallback. A parent component can assign a callback method to a child component's EventCallback.
The following Child
component demonstrates how a button's onclick
handler is set up to receive an EventCallback delegate from the sample's ParentComponent
. The EventCallback is typed with MouseEventArgs
, which is appropriate for an onclick
event from a peripheral device.
Shared/Child.razor
:
<p>
<button @onclick="OnClickCallback">
Trigger a Parent component method
</button>
</p>
@code {
[Parameter]
public string? Title { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
The Parent
component sets the child's EventCallback<TValue> (OnClickCallback
) to its ShowMessage
method.
Pages/Parent.razor
:
@page "/parent"
<h1>Parent-child example</h1>
<Child Title="Panel Title from Parent" OnClickCallback="@ShowMessage">
Content of the child component is supplied by the parent component.
</Child>
<p>@message</p>
@code {
private string? message;
private void ShowMessage(MouseEventArgs e)
{
message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
}
}
When the button is selected in the ChildComponent
:
- The
Parent
component'sShowMessage
method is called.message
is updated and displayed in theParent
component. - A call to
StateHasChanged
isn't required in the callback's method (ShowMessage
). StateHasChanged is called automatically to rerender theParent
component, just as child events trigger component rerendering in event handlers that execute within the child. For more information, see ASP.NET Core Razor component rendering.
EventCallback and EventCallback<TValue> permit asynchronous delegates. EventCallback is weakly typed and allows passing any type argument in InvokeAsync(Object)
. EventCallback<TValue> is strongly typed and requires passing a T
argument in InvokeAsync(T)
that's assignable to TValue
.
<ChildComponent
OnClickCallback="@(async () => { await Task.Yield(); messageText = "Blaze It!"; })" />
Invoke an EventCallback or EventCallback<TValue> with InvokeAsync and await the Task:
await OnClickCallback.InvokeAsync(arg);
Use EventCallback and EventCallback<TValue> for event handling and binding component parameters.
Prefer the strongly typed EventCallback<TValue> over EventCallback. EventCallback<TValue> provides enhanced error feedback to users of the component. Similar to other UI event handlers, specifying the event parameter is optional. Use EventCallback when there's no value passed to the callback.
Prevent default actions
Use the @on{DOM EVENT}:preventDefault
directive attribute to prevent the default action for an event, where the {DOM EVENT}
placeholder is a Document Object Model (DOM) event.
When a key is selected on an input device and the element focus is on a text box, a browser normally displays the key's character in the text box. In the following example, the default behavior is prevented by specifying the @onkeydown:preventDefault
directive attribute. When the focus is on the <input>
element, the counter increments with the key sequence Shift++. The +
character isn't assigned to the <input>
element's value. For more information on keydown
, see MDN Web Docs: Document: keydown
event.
Pages/EventHandlerExample6.razor
:
@page "/event-handler-example-6"
<p>
<input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
</p>
@code {
private int count = 0;
private void KeyHandler(KeyboardEventArgs e)
{
if (e.Key == "+")
{
count++;
}
}
}
Specifying the @on{DOM EVENT}:preventDefault
attribute without a value is equivalent to @on{DOM EVENT}:preventDefault="true"
.
An expression is also a permitted value of the attribute. In the following example, shouldPreventDefault
is a bool
field set to either true
or false
:
<input @onkeydown:preventDefault="shouldPreventDefault" />
...
@code {
private bool shouldPreventDefault = true;
}
Stop event propagation
Use the @on{DOM EVENT}:stopPropagation
directive attribute to stop event propagation within the Blazor scope. {DOM EVENT}
is a placeholder for a Document Object Model (DOM) event.
The stopPropagation
directive attribute's effect is limited to the Blazor scope and doesn't extend to the HTML DOM. Events must propagate to the HTML DOM root before Blazor can act upon them. For a mechanism to prevent HTML DOM event propagation, consider the following approach:
- Obtain the event's path by calling
Event.composedPath()
. - Filter events based on the composed event targets (
EventTarget
).
In the following example, selecting the checkbox prevents click events from the second child <div>
from propagating to the parent <div>
. Since propagated click events normally fire the OnSelectParentDiv
method, selecting the second child <div>
results in the parent <div>
message appearing unless the checkbox is selected.
Pages/EventHandlerExample7.razor
:
@page "/event-handler-example-7"
<label>
<input @bind="stopPropagation" type="checkbox" />
Stop Propagation
</label>
<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
<h3>Parent div</h3>
<div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
Child div that doesn't stop propagation when selected.
</div>
<div class="m-1 p-1 border" @onclick="OnSelectChildDiv"
@onclick:stopPropagation="stopPropagation">
Child div that stops propagation when selected.
</div>
</div>
<p>
@message
</p>
@code {
private bool stopPropagation = false;
private string? message;
private void OnSelectParentDiv() =>
message = $"The parent div was selected. {DateTime.Now}";
private void OnSelectChildDiv() =>
message = $"A child div was selected. {DateTime.Now}";
}
Focus an element
Call FocusAsync on an element reference to focus an element in code. In the following example, select the button to focus the <input>
element.
Pages/EventHandlerExample8.razor
:
@page "/event-handler-example-8"
<p>
<input @ref="exampleInput" />
</p>
<button @onclick="ChangeFocus">
Focus the Input Element
</button>
@code {
private ElementReference exampleInput;
private async Task ChangeFocus()
{
await exampleInput.FocusAsync();
}
}
Specify delegate event handlers in Razor component markup with @on{DOM EVENT}="{DELEGATE}"
Razor syntax:
- The
{DOM EVENT}
placeholder is a Document Object Model (DOM) event (for example,click
). - The
{DELEGATE}
placeholder is the C# delegate event handler.
For event handling:
- Asynchronous delegate event handlers that return a Task are supported.
- Delegate event handlers automatically trigger a UI render, so there's no need to manually call
StateHasChanged
. - Exceptions are logged.
The following code:
- Calls the
UpdateHeading
method when the button is selected in the UI. - Calls the
CheckChanged
method when the checkbox is changed in the UI.
Pages/EventHandlerExample1.razor
:
@page "/event-handler-example-1"
<h1>@currentHeading</h1>
<p>
<label>
New title
<input @bind="newHeading" />
</label>
<button @onclick="UpdateHeading">
Update heading
</button>
</p>
<p>
<label>
<input type="checkbox" @onchange="CheckChanged" />
@checkedMessage
</label>
</p>
@code {
private string currentHeading = "Initial heading";
private string newHeading;
private string checkedMessage = "Not changed yet";
private void UpdateHeading()
{
currentHeading = $"{newHeading}!!!";
}
private void CheckChanged()
{
checkedMessage = $"Last changed at {DateTime.Now}";
}
}
In the following example, UpdateHeading
:
- Is called asynchronously when the button is selected.
- Waits two seconds before updating the heading.
Pages/EventHandlerExample2.razor
:
@page "/event-handler-example-2"
<h1>@currentHeading</h1>
<p>
<label>
New title
<input @bind="newHeading" />
</label>
<button @onclick="UpdateHeading">
Update heading
</button>
</p>
@code {
private string currentHeading = "Initial heading";
private string newHeading;
private async Task UpdateHeading()
{
await Task.Delay(2000);
currentHeading = $"{newHeading}!!!";
}
}
Event arguments
For events that support an event argument type, specifying an event parameter in the event method definition is only necessary if the event type is used in the method. In the following example, MouseEventArgs is used in the ReportPointerLocation
method to set message text that reports the mouse coordinates when the user selects a button in the UI.
Pages/EventHandlerExample3.razor
:
@page "/event-handler-example-3"
@for (var i = 0; i < 4; i++)
{
<p>
<button @onclick="ReportPointerLocation">
Where's my mouse pointer for this button?
</button>
</p>
}
<p>@mousePointerMessage</p>
@code {
private string mousePointerMessage;
private void ReportPointerLocation(MouseEventArgs e)
{
mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
}
}
Supported EventArgs are shown in the following table.
Event | Class | Document Object Model (DOM) events and notes |
---|---|---|
Clipboard | ClipboardEventArgs | oncut , oncopy , onpaste |
Drag | DragEventArgs | ondrag , ondragstart , ondragenter , ondragleave , ondragover , ondrop , ondragend DataTransfer and DataTransferItem hold dragged item data. Implement drag and drop in Blazor apps using JS interop with HTML Drag and Drop API. |
Error | ErrorEventArgs | onerror |
Event | EventArgs | Generalonactivate , onbeforeactivate , onbeforedeactivate , ondeactivate , onfullscreenchange , onfullscreenerror , onloadeddata , onloadedmetadata , onpointerlockchange , onpointerlockerror , onreadystatechange , onscroll Clipboard onbeforecut , onbeforecopy , onbeforepaste Input oninvalid , onreset , onselect , onselectionchange , onselectstart , onsubmit Media oncanplay , oncanplaythrough , oncuechange , ondurationchange , onemptied , onended , onpause , onplay , onplaying , onratechange , onseeked , onseeking , onstalled , onstop , onsuspend , ontimeupdate , ontoggle , onvolumechange , onwaiting EventHandlers holds attributes to configure the mappings between event names and event argument types. |
Focus | FocusEventArgs | onfocus , onblur , onfocusin , onfocusout Doesn't include support for relatedTarget . |
Input | ChangeEventArgs | onchange , oninput |
Keyboard | KeyboardEventArgs | onkeydown , onkeypress , onkeyup |
Mouse | MouseEventArgs | onclick , oncontextmenu , ondblclick , onmousedown , onmouseup , onmouseover , onmousemove , onmouseout |
Mouse pointer | PointerEventArgs | onpointerdown , onpointerup , onpointercancel , onpointermove , onpointerover , onpointerout , onpointerenter , onpointerleave , ongotpointercapture , onlostpointercapture |
Mouse wheel | WheelEventArgs | onwheel , onmousewheel |
Progress | ProgressEventArgs | onabort , onload , onloadend , onloadstart , onprogress , ontimeout |
Touch | TouchEventArgs | ontouchstart , ontouchend , ontouchmove , ontouchenter , ontouchleave , ontouchcancel TouchPoint represents a single contact point on a touch-sensitive device. |
For more information, see EventArgs
classes in the ASP.NET Core reference source (dotnet/aspnetcore main
branch)
Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
Lambda expressions
Lambda expressions are supported as the delegate event handler.
Pages/EventHandlerExample4.razor
:
@page "/event-handler-example-4"
<h1>@heading</h1>
<p>
<button @onclick="@(e => heading = "New heading!!!")">
Update heading
</button>
</p>
@code {
private string heading = "Initial heading";
}
It's often convenient to close over additional values using C# method parameters, such as when iterating over a set of elements. The following example creates three buttons, each of which calls UpdateHeading
and passes the following data:
- An event argument (MouseEventArgs) in
e
. - The button number in
buttonNumber
.
Pages/EventHandlerExample5.razor
:
@page "/event-handler-example-5"
<h1>@heading</h1>
@for (var i = 1; i < 4; i++)
{
var buttonNumber = i;
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@i
</button>
</p>
}
@code {
private string heading = "Select a button to learn its position";
private void UpdateHeading(MouseEventArgs e, int buttonNumber)
{
heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
}
}
Creating a large number of event delegates in a loop may cause poor rendering performance. For more information, see ASP.NET Core Blazor performance best practices.
Avoid using a loop variable directly in a lambda expression, such as i
in the preceding for
loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Capture the variable's value in a local variable. In the preceding example:
- The loop variable
i
is assigned tobuttonNumber
. buttonNumber
is used in the lambda expression.
Alternatively, use a foreach
loop with Enumerable.Range, which doesn't suffer from the preceding problem:
@foreach (var buttonNumber in Enumerable.Range(1,3))
{
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@buttonNumber
</button>
</p>
}
EventCallback
A common scenario with nested components executes a parent component's method when a child component event occurs. An onclick
event occurring in the child component is a common use case. To expose events across components, use an EventCallback. A parent component can assign a callback method to a child component's EventCallback.
The following Child
component demonstrates how a button's onclick
handler is set up to receive an EventCallback delegate from the sample's ParentComponent
. The EventCallback is typed with MouseEventArgs
, which is appropriate for an onclick
event from a peripheral device.
Shared/Child.razor
:
<p>
<button @onclick="OnClickCallback">
Trigger a Parent component method
</button>
</p>
@code {
[Parameter]
public string Title { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
The Parent
component sets the child's EventCallback<TValue> (OnClickCallback
) to its ShowMessage
method.
Pages/Parent.razor
:
@page "/parent"
<h1>Parent-child example</h1>
<Child Title="Panel Title from Parent" OnClickCallback="@ShowMessage">
Content of the child component is supplied by the parent component.
</Child>
<p>@message</p>
@code {
private string message;
private void ShowMessage(MouseEventArgs e)
{
message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
}
}
When the button is selected in the ChildComponent
:
- The
Parent
component'sShowMessage
method is called.message
is updated and displayed in theParent
component. - A call to
StateHasChanged
isn't required in the callback's method (ShowMessage
). StateHasChanged is called automatically to rerender theParent
component, just as child events trigger component rerendering in event handlers that execute within the child. For more information, see ASP.NET Core Razor component rendering.
EventCallback and EventCallback<TValue> permit asynchronous delegates. EventCallback is weakly typed and allows passing any type argument in InvokeAsync(Object)
. EventCallback<TValue> is strongly typed and requires passing a T
argument in InvokeAsync(T)
that's assignable to TValue
.
<ChildComponent
OnClickCallback="@(async () => { await Task.Yield(); messageText = "Blaze It!"; })" />
Invoke an EventCallback or EventCallback<TValue> with InvokeAsync and await the Task:
await OnClickCallback.InvokeAsync(arg);
Use EventCallback and EventCallback<TValue> for event handling and binding component parameters.
Prefer the strongly typed EventCallback<TValue> over EventCallback. EventCallback<TValue> provides enhanced error feedback to users of the component. Similar to other UI event handlers, specifying the event parameter is optional. Use EventCallback when there's no value passed to the callback.
Prevent default actions
Use the @on{DOM EVENT}:preventDefault
directive attribute to prevent the default action for an event, where the {DOM EVENT}
placeholder is a Document Object Model (DOM) event.
When a key is selected on an input device and the element focus is on a text box, a browser normally displays the key's character in the text box. In the following example, the default behavior is prevented by specifying the @onkeydown:preventDefault
directive attribute. When the focus is on the <input>
element, the counter increments with the key sequence Shift++. The +
character isn't assigned to the <input>
element's value. For more information on keydown
, see MDN Web Docs: Document: keydown
event.
Pages/EventHandlerExample6.razor
:
@page "/event-handler-example-6"
<p>
<input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
</p>
@code {
private int count = 0;
private void KeyHandler(KeyboardEventArgs e)
{
if (e.Key == "+")
{
count++;
}
}
}
Specifying the @on{DOM EVENT}:preventDefault
attribute without a value is equivalent to @on{DOM EVENT}:preventDefault="true"
.
An expression is also a permitted value of the attribute. In the following example, shouldPreventDefault
is a bool
field set to either true
or false
:
<input @onkeydown:preventDefault="shouldPreventDefault" />
...
@code {
private bool shouldPreventDefault = true;
}
Stop event propagation
Use the @on{DOM EVENT}:stopPropagation
directive attribute to stop event propagation within the Blazor scope. {DOM EVENT}
is a placeholder for a Document Object Model (DOM) event.
The stopPropagation
directive attribute's effect is limited to the Blazor scope and doesn't extend to the HTML DOM. Events must propagate to the HTML DOM root before Blazor can act upon them. For a mechanism to prevent HTML DOM event propagation, consider the following approach:
- Obtain the event's path by calling
Event.composedPath()
. - Filter events based on the composed event targets (
EventTarget
).
In the following example, selecting the checkbox prevents click events from the second child <div>
from propagating to the parent <div>
. Since propagated click events normally fire the OnSelectParentDiv
method, selecting the second child <div>
results in the parent <div>
message appearing unless the checkbox is selected.
Pages/EventHandlerExample7.razor
:
@page "/event-handler-example-7"
<label>
<input @bind="stopPropagation" type="checkbox" />
Stop Propagation
</label>
<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
<h3>Parent div</h3>
<div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
Child div that doesn't stop propagation when selected.
</div>
<div class="m-1 p-1 border" @onclick="OnSelectChildDiv"
@onclick:stopPropagation="stopPropagation">
Child div that stops propagation when selected.
</div>
</div>
<p>
@message
</p>
@code {
private bool stopPropagation = false;
private string message;
private void OnSelectParentDiv() =>
message = $"The parent div was selected. {DateTime.Now}";
private void OnSelectChildDiv() =>
message = $"A child div was selected. {DateTime.Now}";
}
Focus an element
Call FocusAsync on an element reference to focus an element in code. In the following example, select the button to focus the <input>
element.
Pages/EventHandlerExample8.razor
:
@page "/event-handler-example-8"
<p>
<input @ref="exampleInput" />
</p>
<button @onclick="ChangeFocus">
Focus the Input Element
</button>
@code {
private ElementReference exampleInput;
private async Task ChangeFocus()
{
await exampleInput.FocusAsync();
}
}
Specify delegate event handlers in Razor component markup with @on{DOM EVENT}="{DELEGATE}"
Razor syntax:
- The
{DOM EVENT}
placeholder is a Document Object Model (DOM) event (for example,click
). - The
{DELEGATE}
placeholder is the C# delegate event handler.
For event handling:
- Asynchronous delegate event handlers that return a Task are supported.
- Delegate event handlers automatically trigger a UI render, so there's no need to manually call
StateHasChanged
. - Exceptions are logged.
The following code:
- Calls the
UpdateHeading
method when the button is selected in the UI. - Calls the
CheckChanged
method when the checkbox is changed in the UI.
Pages/EventHandlerExample1.razor
:
@page "/event-handler-example-1"
<h1>@currentHeading</h1>
<p>
<label>
New title
<input @bind="newHeading" />
</label>
<button @onclick="UpdateHeading">
Update heading
</button>
</p>
<p>
<label>
<input type="checkbox" @onchange="CheckChanged" />
@checkedMessage
</label>
</p>
@code {
private string currentHeading = "Initial heading";
private string newHeading;
private string checkedMessage = "Not changed yet";
private void UpdateHeading()
{
currentHeading = $"{newHeading}!!!";
}
private void CheckChanged()
{
checkedMessage = $"Last changed at {DateTime.Now}";
}
}
In the following example, UpdateHeading
:
- Is called asynchronously when the button is selected.
- Waits two seconds before updating the heading.
Pages/EventHandlerExample2.razor
:
@page "/event-handler-example-2"
<h1>@currentHeading</h1>
<p>
<label>
New title
<input @bind="newHeading" />
</label>
<button @onclick="UpdateHeading">
Update heading
</button>
</p>
@code {
private string currentHeading = "Initial heading";
private string newHeading;
private async Task UpdateHeading()
{
await Task.Delay(2000);
currentHeading = $"{newHeading}!!!";
}
}
Event arguments
For events that support an event argument type, specifying an event parameter in the event method definition is only necessary if the event type is used in the method. In the following example, MouseEventArgs is used in the ReportPointerLocation
method to set message text that reports the mouse coordinates when the user selects a button in the UI.
Pages/EventHandlerExample3.razor
:
@page "/event-handler-example-3"
@for (var i = 0; i < 4; i++)
{
<p>
<button @onclick="ReportPointerLocation">
Where's my mouse pointer for this button?
</button>
</p>
}
<p>@mousePointerMessage</p>
@code {
private string mousePointerMessage;
private void ReportPointerLocation(MouseEventArgs e)
{
mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
}
}
Supported EventArgs are shown in the following table.
Event | Class | Document Object Model (DOM) events and notes |
---|---|---|
Clipboard | ClipboardEventArgs | oncut , oncopy , onpaste |
Drag | DragEventArgs | ondrag , ondragstart , ondragenter , ondragleave , ondragover , ondrop , ondragend DataTransfer and DataTransferItem hold dragged item data. Implement drag and drop in Blazor apps using JS interop with HTML Drag and Drop API. |
Error | ErrorEventArgs | onerror |
Event | EventArgs | Generalonactivate , onbeforeactivate , onbeforedeactivate , ondeactivate , onfullscreenchange , onfullscreenerror , onloadeddata , onloadedmetadata , onpointerlockchange , onpointerlockerror , onreadystatechange , onscroll Clipboard onbeforecut , onbeforecopy , onbeforepaste Input oninvalid , onreset , onselect , onselectionchange , onselectstart , onsubmit Media oncanplay , oncanplaythrough , oncuechange , ondurationchange , onemptied , onended , onpause , onplay , onplaying , onratechange , onseeked , onseeking , onstalled , onstop , onsuspend , ontimeupdate , onvolumechange , onwaiting EventHandlers holds attributes to configure the mappings between event names and event argument types. |
Focus | FocusEventArgs | onfocus , onblur , onfocusin , onfocusout Doesn't include support for relatedTarget . |
Input | ChangeEventArgs | onchange , oninput |
Keyboard | KeyboardEventArgs | onkeydown , onkeypress , onkeyup |
Mouse | MouseEventArgs | onclick , oncontextmenu , ondblclick , onmousedown , onmouseup , onmouseover , onmousemove , onmouseout |
Mouse pointer | PointerEventArgs | onpointerdown , onpointerup , onpointercancel , onpointermove , onpointerover , onpointerout , onpointerenter , onpointerleave , ongotpointercapture , onlostpointercapture |
Mouse wheel | WheelEventArgs | onwheel , onmousewheel |
Progress | ProgressEventArgs | onabort , onload , onloadend , onloadstart , onprogress , ontimeout |
Touch | TouchEventArgs | ontouchstart , ontouchend , ontouchmove , ontouchenter , ontouchleave , ontouchcancel TouchPoint represents a single contact point on a touch-sensitive device. |
For more information, see EventArgs
classes in the ASP.NET Core reference source (dotnet/aspnetcore main
branch)
Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
Lambda expressions
Lambda expressions are supported as the delegate event handler.
Pages/EventHandlerExample4.razor
:
@page "/event-handler-example-4"
<h1>@heading</h1>
<p>
<button @onclick="@(e => heading = "New heading!!!")">
Update heading
</button>
</p>
@code {
private string heading = "Initial heading";
}
It's often convenient to close over additional values using C# method parameters, such as when iterating over a set of elements. The following example creates three buttons, each of which calls UpdateHeading
and passes the following data:
- An event argument (MouseEventArgs) in
e
. - The button number in
buttonNumber
.
Pages/EventHandlerExample5.razor
:
@page "/event-handler-example-5"
<h1>@heading</h1>
@for (var i = 1; i < 4; i++)
{
var buttonNumber = i;
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@i
</button>
</p>
}
@code {
private string heading = "Select a button to learn its position";
private void UpdateHeading(MouseEventArgs e, int buttonNumber)
{
heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
}
}
Creating a large number of event delegates in a loop may cause poor rendering performance. For more information, see ASP.NET Core Blazor performance best practices.
Avoid using a loop variable directly in a lambda expression, such as i
in the preceding for
loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Capture the variable's value in a local variable. In the preceding example:
- The loop variable
i
is assigned tobuttonNumber
. buttonNumber
is used in the lambda expression.
Alternatively, use a foreach
loop with Enumerable.Range, which doesn't suffer from the preceding problem:
@foreach (var buttonNumber in Enumerable.Range(1,3))
{
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@buttonNumber
</button>
</p>
}
EventCallback
A common scenario with nested components executes a parent component's method when a child component event occurs. An onclick
event occurring in the child component is a common use case. To expose events across components, use an EventCallback. A parent component can assign a callback method to a child component's EventCallback.
The following Child
component demonstrates how a button's onclick
handler is set up to receive an EventCallback delegate from the sample's ParentComponent
. The EventCallback is typed with MouseEventArgs
, which is appropriate for an onclick
event from a peripheral device.
Shared/Child.razor
:
<p>
<button @onclick="OnClickCallback">
Trigger a Parent component method
</button>
</p>
@code {
[Parameter]
public string Title { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
The Parent
component sets the child's EventCallback<TValue> (OnClickCallback
) to its ShowMessage
method.
Pages/Parent.razor
:
@page "/parent"
<h1>Parent-child example</h1>
<Child Title="Panel Title from Parent" OnClickCallback="@ShowMessage">
Content of the child component is supplied by the parent component.
</Child>
<p>@message</p>
@code {
private string message;
private void ShowMessage(MouseEventArgs e)
{
message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
}
}
When the button is selected in the ChildComponent
:
- The
Parent
component'sShowMessage
method is called.message
is updated and displayed in theParent
component. - A call to
StateHasChanged
isn't required in the callback's method (ShowMessage
). StateHasChanged is called automatically to rerender theParent
component, just as child events trigger component rerendering in event handlers that execute within the child. For more information, see ASP.NET Core Razor component rendering.
EventCallback and EventCallback<TValue> permit asynchronous delegates. EventCallback is weakly typed and allows passing any type argument in InvokeAsync(Object)
. EventCallback<TValue> is strongly typed and requires passing a T
argument in InvokeAsync(T)
that's assignable to TValue
.
<ChildComponent
OnClickCallback="@(async () => { await Task.Yield(); messageText = "Blaze It!"; })" />
Invoke an EventCallback or EventCallback<TValue> with InvokeAsync and await the Task:
await OnClickCallback.InvokeAsync(arg);
Use EventCallback and EventCallback<TValue> for event handling and binding component parameters.
Prefer the strongly typed EventCallback<TValue> over EventCallback. EventCallback<TValue> provides enhanced error feedback to users of the component. Similar to other UI event handlers, specifying the event parameter is optional. Use EventCallback when there's no value passed to the callback.
Prevent default actions
Use the @on{DOM EVENT}:preventDefault
directive attribute to prevent the default action for an event, where the {DOM EVENT}
placeholder is a Document Object Model (DOM) event.
When a key is selected on an input device and the element focus is on a text box, a browser normally displays the key's character in the text box. In the following example, the default behavior is prevented by specifying the @onkeydown:preventDefault
directive attribute. When the focus is on the <input>
element, the counter increments with the key sequence Shift++. The +
character isn't assigned to the <input>
element's value. For more information on keydown
, see MDN Web Docs: Document: keydown
event.
Pages/EventHandlerExample6.razor
:
@page "/event-handler-example-6"
<p>
<input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
</p>
@code {
private int count = 0;
private void KeyHandler(KeyboardEventArgs e)
{
if (e.Key == "+")
{
count++;
}
}
}
Specifying the @on{DOM EVENT}:preventDefault
attribute without a value is equivalent to @on{DOM EVENT}:preventDefault="true"
.
An expression is also a permitted value of the attribute. In the following example, shouldPreventDefault
is a bool
field set to either true
or false
:
<input @onkeydown:preventDefault="shouldPreventDefault" />
...
@code {
private bool shouldPreventDefault = true;
}
Stop event propagation
Use the @on{DOM EVENT}:stopPropagation
directive attribute to stop event propagation within the Blazor scope. {DOM EVENT}
is a placeholder for a Document Object Model (DOM) event.
The stopPropagation
directive attribute's effect is limited to the Blazor scope and doesn't extend to the HTML DOM. Events must propagate to the HTML DOM root before Blazor can act upon them. For a mechanism to prevent HTML DOM event propagation, consider the following approach:
- Obtain the event's path by calling
Event.composedPath()
. - Filter events based on the composed event targets (
EventTarget
).
In the following example, selecting the checkbox prevents click events from the second child <div>
from propagating to the parent <div>
. Since propagated click events normally fire the OnSelectParentDiv
method, selecting the second child <div>
results in the parent <div>
message appearing unless the checkbox is selected.
Pages/EventHandlerExample7.razor
:
@page "/event-handler-example-7"
<label>
<input @bind="stopPropagation" type="checkbox" />
Stop Propagation
</label>
<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
<h3>Parent div</h3>
<div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
Child div that doesn't stop propagation when selected.
</div>
<div class="m-1 p-1 border" @onclick="OnSelectChildDiv"
@onclick:stopPropagation="stopPropagation">
Child div that stops propagation when selected.
</div>
</div>
<p>
@message
</p>
@code {
private bool stopPropagation = false;
private string message;
private void OnSelectParentDiv() =>
message = $"The parent div was selected. {DateTime.Now}";
private void OnSelectChildDiv() =>
message = $"A child div was selected. {DateTime.Now}";
}
Feedback
Submit and view feedback for