This document discusses some internals of the focus system in Qooxdoo since 1.2. This is a technology documentation targeted to interested developers. There is no need to understand these details as a user of the framework.
In previous versions of the focus handling, we forced the application to our own implementation instead of working together with the browser. This was quite straightforward because the topic itself is quite complex and the differences between the browsers are huge. Ignoring all these differences and implementing an own layer was highly attractive. This came with quite some costs, however. For example, it's quite hard to catch all the edge cases when an input field loses the focus; nor is it possible to recover the focus correctly when the browser does something after switching the window (send back/bring to front etc.). To listen on the browser might improve some types of out-of-sync problems in the previous versions. We caught most things correctly, but it was difficult to get 100% accuracy.
With 1.2 the focus system was reimplemented using the new low level event stack. Compared to the old focus system this basically means that the whole focus support is implemented low-level without any dependencies on the widget system. It directly uses the new event infrastructure and integrates well with the other event handlers.
The new system tries to connect with all available native events which could
help with detecting where the browser's focus is moving to. The implementation
makes use of native events like activate
and focusin
where available. It
uses a lot of browser behavior which is not explicitly documented or valid when
reading the specifications, just to solve the issue of detecting where the focus
currently is or is moved to.
It supports the events focusin
, focus
, focusout
and blur
on DOM nodes.
It also supports focus
and blur
events on the window
. There is support for
activate
and deactivate
events on DOM nodes to track keyboard activation. It
has the properties focus
and active
to ask for the currently focused or
activated DOM node.
The activation, as part of the focus system, is also done by this manager. The keyboard handler, for example, asks the focus system which DOM element is the active one, to start the bubble sequences for all keyboard events on this element. As the keyboard layer sits on top of the DOM and implements the event phases on its own, there is no need to inform the browser about the active DOM node as it is simply not relevant when using this layer. It is also quite important, as in every browser tested, the methods to activate a DOM node (if available at all) might also influence the focus, which creates some problems.
The handler also manages the focus state of the top-level window. It fires the
blur
and focus
events on the window
object, which one can listen to.
Natively, these events are fired all over just by tapping somewhere in the
document. The issue is to detect the real focus
/blur
events. This is
implemented through an internal state representation.
Focus handling in Qooxdoo also solves a lot of related issues. For example, the support for unselectable text is done with the focus handler as well. Normally all text content on a page is selectable (with some exceptions like native form buttons etc.). In a typical GUI or during drag&drop sessions it is necessary to stop the user from being able to select any text.
The only thing needed for the focus handler, here, is to add an attribute
qxSelectable
with the value off
, to the node which should not be
selectable..
Behind the scenes Qooxdoo dynamically applies styles like user-select
or
attributes like unselectable
. There are a lot of bugs in the browser when
keeping these attributes or styles statically applied to the nodes so they are
applied as needed dynamically which works surprisingly well. In Internet
Explorer the handler stops the event selectstart
for the affected elements.
One thing we needed, especially for the widget system which is built on top, was
support for preventing a widget -- or in this case, a DOM node -- from being
able to get the focus. This sounds simpler than it is. The major issue is to
also keep the focus where it is while tapping somewhere else. This is especially
interesting when working with a text selection. Unfortunately in a browser the
selection can only be where the focus is. This is a major issue when trying to
apply any change to the currently selected text, as is needed for most kinds of
editors such as a rich text editor used by a mail application. The type of fix
we apply in Qooxdoo is to not allow the browser to focus a specific DOM node
e.g. the "Bold" button of the text editor. This makes it easy to add listeners
to the button which work with the still existing selection of the editor field.
The feature could be applied easily to a DOM node like such a button, using the
attribute qxKeepFocus
with the value on
. It affects all children of the
element as well, as long as these do not override it.
A similar goal is to keep the activation where it is when the user taps at a
specific section of the document. This is mainly used to keep the keyboard
processing where it is, e.g. when tapping the opened list of a SelectBox
widget. This feature could be used for similar scenarios as well. It, similarly
to qxKeepFocus
, can be enabled simply by setting the attribute qxKeepActive
to on
for the relevant DOM node. Internally, stopping the activation also
means stopping the focus. It was not solvable in another way because the browser
otherwise sends activation events to the focused DOM node which is
counter-productive in this case.
Another unwanted side effect of some browsers is the possibility to drag around
specific types of content. There is some type of native drag&drop support in
most of today's browsers, but this is quite useless with the current quality of
implementation. Still, the major issue remains: It is possible to drag around
images, for example, which is often not wanted in a GUI toolkit. These native
"features" compromise the behavior implemented by the application developer on
top of them. To stop this, Qooxdoo applies styles like user-drag
on browsers
that support it, or prevents the native draggesture
event where available.
Other then this, most needed preventions are implemented internally through a
preventDefault
call on the global pointerdown
event, when a specific target
is detected. This has some side effects though. When preventing such a core
event, it means that most browsers also stop any type of selection happening
through the pointer. This also stops them from focusing the DOM node natively.
The Qooxdoo code uses some explicit focus
calls on the DOM nodes to fix this.
Please note that some settings may have side effects on other things. For example, to make a text region selectable but not activateable is not possible with the current implementation. This tends not to be a hinderance in real-world applications, but may be still interesting to know about.
Finally, the whole implementation differs nearly completely for the supported browsers. Hopefully you get an idea of the complexity of the topic.