Introduction. Event propagation Schema
Sciter uses sinking/bubbling event propagation schema. It is the same schema as used in web browsers.
Let’s consider the following markup:
<html>
<body>
<form>
<input type="text" name="username" />
<button><span>Login</span></button>
</form>
</body>
</html>
When loaded into Sciter’s window (or in web browser), such document will be parsed into so called DOM tree – tree alike structure of elements . Each child element in such tree has strictly one parent but each element may have several children (or none at all). Event dispatching mechanism will use that tree to deliver UI events to elements using parent-child chains.
As an example, here is the flow of mousedown event when mouse click was made on <span>Login</span> element from the markup above:
As outlined the event gets dispatched in two directions:
- In “sinking” phase, from outermost container (
<html>) toEvent.targetelement (<span>with “Login” text in this case) and - in “bubbling” phase, from
Event.targetto its outermost container.
Such event propagation schema allows each container to handle events as before its children as after them .
Sometimes sinking phase is named as “capturing” phase as it allows containers to capture (or intercept) events coming to children. Container can return true from its sinking event handler, in this case children will not receive that event in normal form (but see “raw” event handlers).
“Modern” event handlers
Modern event handlers use event syntax construct to declare blocks of code that handle particular events.
Global event handlers
Global event handlers are code blocks that are defined on global level. Their purpose is to handle events that were not handled by other code. Such event handlers use this syntax:
event ename $(selector) (evt) { ... code ... }
Where:
- ename is an event name, mandatory. One of predefined events or name of custom event;
- $(selector) – filtering CSS expression, optional. If provided the code block will execute only on ename event coming from matching element;
- evt – Event object reference, optional;
- this – is an element matching the selector, or, if it is not provided, this will be set to self – current document.
Example, handling click on button with ID=”login”:
event click $(button#login) {
...
return true; // mark the event as handled
}
Class level event handlers
Such event handlers are parts of “widget” declarations – classes derived from Element class. Such classes control behavior of particular DOM elements that’s why they are also known as scripting behaviors or element controllers.
Main goal of such classes is to control particular DOM elements: handle events and provide additional widget specific methods.
Event handlers defined inside such classes receive events coming as from element itself as from any child inside them:
class SomeWidget : Element
{
event ename $(selector) (evt, selElement) {
... event handling code ...
}
}
Where:
- ename – event name
- $(selector), evt – as the above, optional;
- selElement – used only when selector is defined, arbitrary variable name that will receive reference of the element matching the selector;
- this – environment variable, in class event handlers it is always a reference of the element this class was attached to.
Explicit per-element handlers
In some cases you may want to attach event handlers explicitly to particular DOM elements. That can be done by the following construct:
element << event ename $(selector) (evt)
{
... event handling code ...
};
This way of event handling is useful when you need to handle mouse**** events, especially mouseenter and mouseleave events that are dispatched to the element itself – the one that gets or looses mouse pointer.
Note that global and class event handlers are preferable if you want to handle events from group of similar elements so this:
event click $(ul#foo > li) { .... }
single handler will handle clicks on any <li> element in <ul #foo> but this
for( var li in $(ul#foo) )
li << event click { .... }
will unnecessary attach handlers to each <li> in the list.
Handling events in different phases.
By default event handlers will be triggered on non-handled events in bubbling phase, so if event was already handled by some child (its event handler returns true) the event will not hit container’s handler.
In order to handle the event in sinking phase (so before it will be dispatched to children) the event name shall be prepended by ~ sign:
event ~click $(ul#foo > li) { .... }
“jQuery” alike event handlers
As an alternative Sciter also supports functional style of event handler assignment using Element.on() method:
element.on("ename", "selector", function(evt) { ... code ...});
Where:
- ename is the same event name but as a string literal;
- selector is an optional CSS selector. given as a string;
- and function is an ordinary script function that will be called when the event will occur.
- this variable in handling function will take reference of either element matching the selector or the element itself.
To handle event in sinking phase prepend its name by ~ sign, so “click” will handle unhandled click event in bubbling phase but “~click” is the click in sinking phase – in parent first order.
As you see this on() function is a method of DOM Element class so it always attaches the handler to some particular element. In order to setup global event handler you shall attach it to the root of DOM tree – to self that is root document node:
self.on("click", "ul#foo > li", function(evt) {
// this here is a li element in ul#foo ... code ... });
“Raw” event handlers
As a low level mechanism raw event handlers allow to handle all events in all phases, even those that were marked as handled. Note: “modern” and “functional” event handlers will not receive handled events – only non-handled.
Raw event handlers can be set as on particular elements like:
var someElement = ...;
someElement.onMouse = function(evt) {...}
or as a method of custom behavioral classes:
class MyWidget : Element {
function onMouse(evt) {....}
}
Raw event handler “ports”
Here is full list of ports that can be used to setup custom event handlers:
| Event group | Handler port |
|---|---|
| mouse events | onMouse(evt) |
| keyboard events | onKey(evt) |
| scroll events | onScroll(evt) |
| focus events | onFocus(evt) |
logical (synthetic) events like Event.BUTTON_CLICK |
onControlEvent(evt) |
system drag-n-drop events like Event.X_DRAG |
onExchange(evt) |
| touch events | onGesture(evt) |
| editing commands events | onCommand(evt) |
Here is typical code of raw event handler listing all possible life-cycle variations of MOUSE_DOWN event:
class Widget : Element {
function onMouse(evt) {
switch(evt.type) {
case Event.MOUSE_DOWN: ...; break; // not handled mouse down event in bubbling phase
case Event.SINKING | Event.MOUSE_DOWN: ...; break; // not handled mouse down event in sinking phase
case Event.HANDLED | Event.MOUSE_DOWN: ...; break; // handled mouse down event in bubbling phase
case Event.HANDLED | Event.SINKING | Event.MOUSE_DOWN: ...; break; // handled mouse down event in sinking phase
}
}
}
As you see it allows to handle events in sinking and bubbling phases and to determine if event was handled by other handlers in the chain.
Multiple event handlers
Each DOM element may have several “modern” and “functional” event handlers but only one “raw” handler.
If some element has multiple event handlers of the same event then all matching handlers will be called. Order of such calls is deliberately unspecified.
Handlers of custom events
Sciter supports custom events in the same way as pre-defined ones. All event handler methods can be used to handle custom events.
Examples of handlers of custom event named “note-added”:
event note-added (evt) {
var noteId = evt.data;
...
}
Functional handler of the same event:
self.on("note-added", function(evt) {
var noteId = evt.data;
...
})
and raw event handler:
self.onControlEvent = function(function(evt) {
if( evt.type == Event.CUSTOM && evt.match("note-added") ) {
var noteId = evt.data;
}
...
})
Note that custom events use the same sinking/bubbling dispatch propagation, so being sent from some element, they can be handled by handlers on the element and/or on any of its parents up until self DOM root:
var targetElement = ...;
var handled = targetElement.sendEvent("note-added", note.id); // synchronous event generation
targetElement.postEvent("note-added", note.id); // asynchronous event generation - "fire-and-forget"


Build RSS channel
Sciter Twitter channel