In today's world, websites are no longer static HTML sites. The evolution of web technology enables users to have numerous interactions with your website. As a crucial part of building an interactive website, handling events allows your website to respond to user interactions, such as clicks, key presses, and other actions. In this tutorial, you will learn:
preventDefault
).stopPropagation
).There are 2 types of events: HTML events and Blazor events. HTML events are predefined by the browser and allow developers to respond to user actions, such as clicks or key presses. Blazor events build on top of these HTML events. While HTML events are directly tied to user interactions with elements, Blazor events are linked to the business logic of the application. This separation makes it easier to manage application state, trigger multiple actions, and maintain cleaner, more maintainable code.
An HTML event consists of 2 parts: the name and the arguments. The name indicates how to trigger the event, while the arguments contain information about the event itself. For example, you can detect if the user is holding the Ctrl key when clicking a button by examining the event arguments.
Consider the following example:
<button type="button" onclick="alert(`Is holding Ctrl? ${event.ctrlKey}`)">Handle with JavaScript with event args</button>
In this example, the browser will display a message saying "Is holding Ctrl? true" or "Is holding Ctrl? false" based on the user's action. This event is handled using JavaScript.
In Blazor, you can handle the same HTML event using C#. Here's an example that achieves a similar outcome:
<button type="button" @onclick="HandleHTMLEventWithMouseEvent"> Handle with C# with event args (Is holding Ctrl? @isHoldingCtrl) </button> @code { private bool isHoldingCtrl = false; private void HandleHTMLEventWithMouseEvent(MouseEventArgs e) { isHoldingCtrl = e.CtrlKey; } }
To handle an HTML event with C#, you need to declare a method with the corresponding event argument class. In the example above, the C# method HandleHTMLEventWithMouseEvent
is declared with MouseEventArgs
as its parameter to handle the onclick
event. Here's a list of HTML events along with their corresponding C# event argument classes, grouped by event type:
HTML Event | C# Event Argument Class |
---|---|
Focus Event | |
@onfocus |
FocusEventArgs |
@onblur |
FocusEventArgs |
@onfocusin |
FocusEventArgs |
@onfocusout |
FocusEventArgs |
Mouse Event | |
@onmouseover |
MouseEventArgs |
@onmouseout |
MouseEventArgs |
@onmousemove |
MouseEventArgs |
@onmousedown |
MouseEventArgs |
@onmouseup |
MouseEventArgs |
@onclick |
MouseEventArgs |
@ondblclick |
MouseEventArgs |
@onwheel |
WheelEventArgs |
@onmousewheel |
WheelEventArgs |
@oncontextmenu |
MouseEventArgs |
Drag Event | |
@ondrag |
DragEventArgs |
@ondragend |
DragEventArgs |
@ondragenter |
DragEventArgs |
@ondragleave |
DragEventArgs |
@ondragover |
DragEventArgs |
@ondragstart |
DragEventArgs |
@ondrop |
DragEventArgs |
Keyboard Event | |
@onkeydown |
KeyboardEventArgs |
@onkeyup |
KeyboardEventArgs |
@onkeypress |
KeyboardEventArgs |
Input Event | |
@onchange |
ChangeEventArgs |
@oninput |
ChangeEventArgs |
@oninvalid |
EventArgs |
@onreset |
EventArgs |
@onselect |
EventArgs |
@onselectstart |
EventArgs |
@onselectionchange |
EventArgs |
@onsubmit |
EventArgs |
Clipboard Event | |
@onbeforecopy |
EventArgs |
@onbeforecut |
EventArgs |
@onbeforepaste |
EventArgs |
@oncopy |
ClipboardEventArgs |
@oncut |
ClipboardEventArgs |
@onpaste |
ClipboardEventArgs |
Touch Event | |
@ontouchcancel |
TouchEventArgs |
@ontouchend |
TouchEventArgs |
@ontouchmove |
TouchEventArgs |
@ontouchstart |
TouchEventArgs |
@ontouchenter |
TouchEventArgs |
@ontouchleave |
TouchEventArgs |
Pointer Event | |
@ongotpointercapture |
PointerEventArgs |
@onlostpointercapture |
PointerEventArgs |
@onpointercancel |
PointerEventArgs |
@onpointerdown |
PointerEventArgs |
@onpointerenter |
PointerEventArgs |
@onpointerleave |
PointerEventArgs |
@onpointermove |
PointerEventArgs |
@onpointerout |
PointerEventArgs |
@onpointerover |
PointerEventArgs |
@onpointerup |
PointerEventArgs |
Media Event | |
@oncanplay |
EventArgs |
@oncanplaythrough |
EventArgs |
@oncuechange |
EventArgs |
@ondurationchange |
EventArgs |
@onemptied |
EventArgs |
@onpause |
EventArgs |
@onplay |
EventArgs |
@onplaying |
EventArgs |
@onratechange |
EventArgs |
@onseeked |
EventArgs |
@onseeking |
EventArgs |
@onstalled |
EventArgs |
@onstop |
EventArgs |
@onsuspend |
EventArgs |
@ontimeupdate |
EventArgs |
@onvolumechange |
EventArgs |
@onwaiting |
EventArgs |
Progress Event | |
@onloadstart |
ProgressEventArgs |
@ontimeout |
ProgressEventArgs |
@onabort |
ProgressEventArgs |
@onload |
ProgressEventArgs |
@onloadend |
ProgressEventArgs |
@onprogress |
ProgressEventArgs |
@onerror |
ErrorEventArgs |
Other Event | |
@onactivate |
EventArgs |
@onbeforeactivate |
EventArgs |
@onbeforedeactivate |
EventArgs |
@ondeactivate |
EventArgs |
@onended |
EventArgs |
@onfullscreenchange |
EventArgs |
@onfullscreenerror |
EventArgs |
@onloadeddata |
EventArgs |
@onloadedmetadata |
EventArgs |
@onpointerlockchange |
EventArgs |
@onpointerlockerror |
EventArgs |
@onreadystatechange |
EventArgs |
@onscroll |
EventArgs |
@ontoggle |
EventArgs |
You don’t always need to use event arguments, especially if you don’t care about the event details. Declaring an unused event argument can make your code unnecessarily complex. For example, you might not need to know if the user is holding down a key while clicking a button. Fortunately, you can skip the event argument declaration in such cases. Consider the following example:
<button type="button" onclick="alert('Hello Blazor School!')">Handle with JavaScript</button>
In this example, a message is displayed when the user clicks the button. The event is handled by JavaScript, but you can achieve the same outcome with C# in Blazor:
<button type="button" class="btn btn-primary" @onclick="IncrementCount"> Handle with C# (@currentCount clicked) </button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
Just like HTML events, a Blazor event also consists of 2 parts: the name and the argument. However, unlike HTML events, which are predefined by the browser, Blazor events are defined by the developer. Consider the following BlazorDiceRoll.razor component:
<button type="button" @onclick="RollTheDice">Roll Dice!</button> @code { [Parameter] public EventCallback<int> OnDiceRolled { get; set; } public void RollTheDice() => OnDiceRolled.InvokeAsync(new Random().Next(1, 6)); }
In this example, we created the OnDiceRolled
event on top of the HTML onclick
event. This Blazor event is used to notify other components when the dice has been rolled. Here’s how the event can be consumed:
<p>Roll the dice. Current dice face: @currentDiceFace</p> <BlazorDiceRoll OnDiceRolled="UpdateDiceFace" /> @code { private int? currentDiceFace; private void UpdateDiceFace(int diceFace) { currentDiceFace = diceFace; } }
In this example, the UpdateDiceFace
method is called whenever the OnDiceRolled
event is triggered, updating the displayed dice face. This demonstrates how you can create custom Blazor events that communicate between components.
In BlazorDiceRoll.razor
, the OnDiceRolled
event is defined with the type EventCallback<int>
. This means the event is designed to pass a single int
parameter when triggered. Consequently, the UpdateDiceFace
method must also accept a single int
parameter.
If you try to add more parameters to the UpdateDiceFace
method, the project will fail to build. This is because the event and its handler must have matching parameter types and counts for Blazor to correctly bind them together.
Similar to HTML events, Blazor events don’t always require event arguments, which can help simplify your code when they aren't needed. Consider the following BlazorRating.razor
component:
<div> <label><input type="radio" name="rating" @onchange="On5StarsSelected" />*****</label> <label><input type="radio" name="rating" />****</label> <label><input type="radio" name="rating" />***</label> <label><input type="radio" name="rating" />**</label> <label><input type="radio" name="rating" />*</label> </div> @code { [Parameter] public EventCallback On5StarsSelected { get; set; } }
In this example, the On5StarsSelected
event is declared without any event argument. Here's how it can be consumed:
<p>How much do you love Blazor? Select 5 stars to increase this number: @selected5StarsCount</p> <BlazorRating On5StarsSelected="IncrementCount" /> @code { private int selected5StarsCount = 0; private void IncrementCount() { selected5StarsCount++; } }
Each time a user selects 5 stars, the count is incremented by 1. Notice that the IncrementCount
method, which handles the On5StarsSelected
event, has no parameters.
If you try to add parameters to the IncrementCount
method, the project will fail to build. This is because the method signature must exactly match the event's definition, which in this case, has no arguments.
preventDefault
)preventDefault
. For instance, with a checkbox input, clicking it normally causes the browser to tick it—this is its default behavior. Here’s an example that prevents the checkbox from being ticked when the user clicks it.<label> <input type="checkbox" @onclick="_ => showMessage = true" @onclick:preventDefault /> @if (showMessage) { <span>The checkbox can’t be checked because the <code>onclick</code> event was cancelled using <code>@onclick:preventDefault</code>.</span> } else { <span>Click the checkbox</span> } </label> @code { private bool showMessage = false; }
stopPropagation
)HTML elements can be nested at multiple levels, like boxes inside boxes. When you click an element, an event starts at that spot and travels up through its parent elements—this is called event bubbling. In Blazor, you can stop this bubbling with stopPropagation
. Take a look at this example:
<button type="button" @onclick='_ => Message += "<code>Level 1</code> clicked. "'> Level 1 <button type="button" @onclick='_ => Message += "<code>Level 2</code> clicked. "'> Level 2 <button type="button" @onclick='_ => Message += "<code>Level 3</code> clicked. "'> Level 3 </button> </button> </button>
In this example, we have 3 nested buttons, with the Level 1 button as the outermost and Level 3 as the innermost. Imagine the user clicks on Level 3: the onclick
event will propagate from Level 3 to Level 2, and then from Level 2 to Level 1. All 3 buttons register as clicked.
Now, let's add @onclick:stopPropagation
to the Level 2 button. With this change, the propagation goes only from Level 3 to Level 2 and stops there.