@blur
Summary
@blur attaches a handler to the native blur event on an element. The expression on @blur is evaluated when the element loses focus. Typical uses include marking a field as “touched”, running lightweight validation, or committing staged values when the user leaves a control.
@blur is part of the event handler family (such as @click, @input, @change, @focus) and follows the same general evaluation rules as other @ directives.
Basic example
Marking an input as “touched” when it loses focus:
<serc-rod id="app" data='{
"profile": { "name": "" },
"touched": { "name": false }
}'>
<label>
Name:
<input type="text"
:value="profile.name"
@blur="touched.name = true">
</label>
<p *if="touched.name && !profile.name">
Please enter your name.
</p>
</serc-rod>
Behavior:
- The input is bound to
profile.namevia:value. - When the user focuses the input and then moves focus elsewhere, the browser fires a
blurevent. - Sercrod invokes the
@blurexpression in the current scope, settingtouched.name = true. - The paragraph becomes visible if the field is now empty and has been blurred at least once.
Behavior
Core rules:
-
Target event
@blurwires the nativeblurevent on the element where it is declared. The element must be focusable for the handler to fire (for example, inputs, textareas, buttons, links, or elements made focusable viatabindex). -
Expression evaluation
Sercrod parses the attribute value as an expression (for exampletouched.name = trueorsave(profile)). When the event fires, Sercrod evaluates this expression using the same scope model as other directives on that element. -
One way effect
The result of the expression is ignored by Sercrod. What matters is the side effect of the expression (writing to data, calling functions, or both). -
Multiple handlers
You can combine@blurwith other event directives on the same element (such as@focusor@input), as long as each one uses a distinct event name. Each handler is evaluated independently when its event fires.
Sercrod does not change the native timing or semantics of the blur event; it only injects expression evaluation when the event occurs.
Evaluation timing
@blur participates in Sercrod’s normal event lifecycle:
-
Structural directives first
Directives such as*if,*for,*each,*switch, and*includedecide the presence and shape of the element. If an element is removed by a structural directive, no event handler is attached. -
Attribute phase
Once an element is kept, Sercrod processes its attributes. Event attributes starting with@are recognized in the same pass as colon attributes like:valueand directive attributes like*if. -
Listener attachment
During this pass, Sercrod registers ablurlistener for each element with@blur. On re-renders, Sercrod ensures that the handler reflects the current expression and scope. -
Event firing
When the browser firesbluron that element, Sercrod evaluates the stored expression in the current Sercrod scope associated with the host.
There is no special debounce or scheduling around @blur; it runs synchronously when the event fires, in the same turn as the native blur event.
Execution model
Conceptually, the runtime behaves as follows for @blur:
- During rendering, Sercrod finds an element with an attribute named
@blur. - It reads the attribute value as a source string, for example
touched.name = trueorvalidateField('name'). - Sercrod compiles or stores this expression so it can be evaluated later in the current host’s scope.
- Sercrod attaches a native event listener for
"blur"on that element. - When the browser fires
blur:- Sercrod prepares the evaluation context for this host and element.
- Sercrod evaluates the stored expression with its expression evaluator.
- If evaluation throws, Sercrod catches the error and logs it through the configured logging mechanism; the error does not stop other handlers or other elements from updating.
- If
cleanup.handlersis enabled in the configuration, Sercrod may remove the original@blurattribute from the DOM, leaving only the wired listener and any other visible attributes.
The listener itself is lean: it does not try to change the propagation of the event (such as stopping it) unless your expression does so through normal browser APIs.
Use with form fields and data bindings
@blur is often paired with data bindings to form controls:
-
With
:valueor:checked:<input type="text" :value="profile.email" @blur="profile.email = profile.email.trim()">:valuekeeps the DOM value in sync withprofile.email.@blurensures that the value is trimmed when the user leaves the field.
-
With staging via
*stage:<form *stage="'profile'"> <input type="text" :value="profile.name" @blur="profile.name = profile.name.trim()"> <button type="button" *apply="'profile'">Save</button> <button type="button" *restore="'profile'">Cancel</button> </form>Here,
@blurcleans up the staged copy ofprofile.name. Only when the user presses “Save” does*applycommit the staged profile back to main data.
Because @blur runs only when focus leaves the element, it is a good fit for:
- Validation that should not interrupt typing.
- Cleaning up formatting.
- Toggling “touched” flags for UI feedback.
Use with conditionals and loops
@blur can be freely combined with structural directives that control the element itself:
-
Conditional display:
<input type="text" *if="mode === 'edit'" :value="item.title" @blur="item.title_touched = true">The handler is attached only when the
*ifcondition is true. -
Inside loops:
<ul> <li *for="item of items"> <input type="text" :value="item.label" @blur="item.touched = true"> </li> </ul>Each iteration gets its own
@blurhandler, and the expression is evaluated with that iteration’sitemin scope.
If a structural directive replaces the element on re-render (for example via key changes or switching a block), Sercrod re-attaches @blur on the new element instance as part of its normal rendering process.
Sercrod-specific restrictions
For @blur, there are no special Sercrod-only restrictions beyond the general event rules:
- You may put
@bluron any focusable element. - You may combine
@blurwith other event directives on the same element, as long as each uses a different event name (for example@focus,@input,@change). - You may combine
@blurwith attribute bindings like:value,:class, and:style, and with structural directives such as*ifor*forthat target the same element.
Unlike structural directives (*each, *include, *import), event directives do not compete for ownership of the element’s children, so there is no “only one of these” constraint specific to @blur.
Best practices
-
Keep handlers small
Use@blurfor small, local side effects: marking fields as touched, trimming strings, or triggering simple validations. If the logic is large or used in multiple places, move it into a helper function and call that from@blur. -
Avoid heavy work
Sincebluris synchronous, avoid expensive operations (such as large network calls or heavy computation) directly inside the handler expression. Instead, delegate to asynchronous functions or queues if needed. -
Combine with “touched” flags
Use dedicated flags (for exampletouched.name) to decouple user interactions from validation logic. UI components can then react to those flags instead of inferring state from the presence of errors alone. -
Be mindful of non-bubbling behavior
The nativeblurevent does not bubble in the same way asclick. Place@bluron the actual element that should react to losing focus, not on a distant ancestor. -
Use
@focusand@blurtogether when needed
For complex interactions, combine@focusand@bluron the same element to track active state or to highlight inputs while they are being edited.
Additional examples
Mark field as dirty only when value actually changed:
<serc-rod id="app" data='{
"original": { "name": "Taro" },
"current": { "name": "Taro" },
"dirty": { "name": false }
}'>
<input type="text"
:value="current.name"
@blur="dirty.name = (current.name !== original.name)">
</serc-rod>
Local validation on blur:
<serc-rod id="app" data='{
"email": "",
"errors": { "email": "" }
}'>
<input type="email"
:value="email"
@blur="
errors.email =
(!email || email.indexOf('@') === -1)
? 'Please enter a valid email.'
: '';
">
<p *if="errors.email" *print="errors.email"></p>
</serc-rod>
Toggle helper text visibility:
<serc-rod id="app" data='{
"show_help": true
}'>
<input type="text"
:value="''"
@blur="show_help = false">
<p *if="show_help">
You can leave this field empty.
</p>
</serc-rod>
Notes
@bluris an event handler directive that wires the element’s nativeblurevent to a Sercrod expression.- The expression runs in the same scope as other directives on that element and is used for side effects, not for returning values.
@blurcomposes with other directives, including structural ones, without competing for ownership of the element’s children.- If
cleanup.handlersis enabled in the Sercrod configuration, the original@blurattribute is removed from the output DOM after the listener has been attached, keeping the rendered markup clean.