-
Notifications
You must be signed in to change notification settings - Fork 17
Setting focus
WComponents provides a mechanism to request focus be set to any identifiable WComponent. Some components are not intrinsically focusable but may have focusable content and some just aren't focusable. There is also no guarantee that the focus request will be honoured by the client. This page explains how to request focus and how the client works out whether the request should be honoured.
Setting default focus when loading a full view within a web application is controversial as it may result in some users not being provided with adequate contextual information. Under many circumstances focus should not be set at all when loading a page. If default focus is to be set (or requested) when loading a full page (rather than a partial via ajax) then it is the co-responsibility of the designer and developer to ensure they are not causing accessibility problems for any potential user.
Setting default focus on full page load may not be supported to prevent this sort of accessibility issue.
Focus may be requested as part of any response - a full page load from a GET or POST or an ajax response. The mechanism is the same: call setFocussed()
. This is most commonly done as part of an Action or as a java.lang.Runnable
which can then be deferred using AbstractWComponent.runLater(Runnable)
.
// given component foo ask fir it to have default focus
foo.setFocussed();
// ...
// within handleRequest (for example)
// ...
Runnable later = new Runnable() {
@Override
public void run() {
focusMe();
}
};
invokeLater(later);
// ...
// the helper function allows access to the instance
// from within the anonymous class, that is all, you
// know this stuff!
private void focusMe() {
this.setFocussed();
}
Any component can claim default focus on the next load and the last one to stake its claim wins. So if one is setting default focus only if nothing else has it then one ought check first:
// you may consider a null check on UIContextHolder.getCurrent()
// just in case...
if (UIContextHolder.getCurrent().getFocussed() == null) {
setFocussed();
}
Some components will attempt to refocus themselves under some conditions. This has lead to some accessibility and usability problems which has resulted in a change to this in v1.4. WButton and Inputs which are also triggers for a WAjaxControl may try to refocus themselves if:
- the request is an Ajax request;
- the button or input which triggered the request is still available after the response is handled; and
- nothing else has called setFocussed() in the current context.
Any component which implements com.github.bordertech.wcomponents.WComponent
will have setFocussed()
. It makes no sense to try to focus a component which has no UI artefact. It may also appear counter-productive to try to focus a container such as WPanel. The client code will make a decent fist of trying to work out what the application wants to have focused so if focus is requested on a non-focusable component it will try other options as explained below.
Calling setFocussed()
is a request for focus. It may not be acceptable. The main aim of the UI code is to provide the required functionality but tempered with Accessibility and usability concerns. So this is a thumbnail of how acceptance of a focus request is determined:
- If there is already something in the UI which has focus then the focus request is ignored so that the user does not have a sudden change of focus - very annoying if one is entering text and suddenly it is going into the wrong field. This is most likely the case when the response is an AJAX response.
- If the focus request is part of a full page load then the request will be ignored if there is an error message box on the page - in this case the error box may receive focus instead.
- Otherwise the request will be honoured if possible.
If the element to be focussed is focusable, such as a button or link, then it will get focus. If the element is not intrinsically focusable then the following occurs:
-
If the element has focusable content then focus will be set to the first focusable descendant. This is the case for all complex Input controls (not in a read-only state) and may be the case for generic containers if the container has focusable content.
WPanel buttonPanel = new WPanel(); // ... // add button(s) to the buttonPanel // with WButtons maybeDisabledButton and possiblyHiddenButton... buttonPanel.add(maybeDisabledButton); buttonPanel.add(possiblyHiddenButton); //... //... // All the application needs is for the first available button to have default // focus so request focus for the WPanel buttonPanel.setFocussed();
-
If the element does not have focusable content and the element occupies no space in the UI then the nearest focusable ancestor of the element will receive focus. This is useful if, for example, focus is requested for a WMenuItem which may be in a closed WSubMenu. In this case leaf is inside a WMenu of MenuType.BAR so its branches are never open on page load. The focus request will find the nearest focusable ancestor (which would be the branch in this case) and focus it.
WMenu tree = new WMenu(WMenu.MenuType.BAR); // ... WSubMenu aBranch = new WSubMenu("Stuff"); tree.add(branch); // ... and a WMenuItem inside the branch branch.add(leaf); // ... elsewhere // we have interacted with leaf so it makes some sense to refocus it leaf.setFocussed();
- WSkipLinks provides a way to create quick navigation to parts of the WComponents UI which may make it easier to navigate without default focus.