A tale of tab order

The TabOrder property is well known by all Delphi developers. It is part of Delphi controls since Delphi 1. IntraWeb controls also have a TabOrder property that mimics the standard VCL TabOrder property mapping it to the analogous HTML tabindex attribute (in lower case), which does more or less the same. The tabindex attribute also maps directly to the HTMLElement.tabIndex property (camel case), but this is irrelevant in our context.

However, there are some important differences between TabOrder – the Windows control property – and the tabindex – the HTML attribute. The most important is that TabOrder in Windows world is relative to the immediate parent control, i.e., when a control gets a TabOder value of 0, it means that it should be the first to receive focus among all the child controls of that specific parent. Multiple controls in a Form can have the same TabOder as long as they belong to different parent controls. On the other hand, the tabindex attribute is global within the DOM document, which means that each control should have a unique tabindex attribute within the document. HTML allows developers to specify the same tabindex for multiple elements on the page, though. When this happens, the order will be decided according to their relative position within the HTML document, i.e. if two buttons on an HTML page have the same tabindex attribute, the browser will set focus first on the button that comes first in the HTML source code.

Another important difference is that, in HTML, a negative tabindex (regardless of its magnitude), means that the control should not receive focus when navigating using the keyboard. The control is still enabled and the user can interact with it normally, though. This behavior has no similarities with standard VCL controls behavior.

Basically that’s how HTML works regarding tab index:

  • tabindex < 0: control won’t receive focus from keyboard
  • tabindex = 0: control will receive focus but the order will be established by its position within HTML 
  • tabindex > 0: control will receive focus when navigating via keyboard and the order will be established by tabindex value
  • When multiple controls have the same tabindex >= 0, the order is established by their relative order within HTML document

The word “control” is freely used above when the exact term is actually “HTML element” because even something that is not a “control” can be forced to receive focus, for instance, a DIV element.

It is also considered an anti-pattern in HTML when all elements receive an identical tabindex = 0, meaning that the HTML source contains lots of useless information. Some HTML experts also claim that tabindex should be seldom used if it should be used at all… Some consider that HTML writers should just let the browser figure out what’s the best order based on element position within the HTML page. In general, browsers are good at it indeed.

Now, back to Intraweb. As mentioned earlier, IntraWeb tries to mimic the same behavior that Delphi users are used to having in VCL applications, but recreating the exact same behavior is counterproductive and does not play well with HTML standards. Instead, IntraWeb just makes it simple for users to set the order of tabulation, exactly the way they expect it to be.

In the latest IW branch 15.2 we started refactoring all code related to TabOrder setting – at design time – and tabindex rendering – at runtime, making it not only simpler but also more performant. The work has been completed in IW 15.2.13 just released.

In this version, we introduced a new IWForm property named TabOrderThreshold (Smallint, default is 0). This is a simple way to tell IntraWeb what TabOrder values you want to be rendered on the final HTML source. 

  • Any control which TabOrder property value is less than or equal to IWForm’s TabOrderThreshold won’t render the tabindex attribute.

So, if an IWForm TabOderThreshold is zero, only controls with positive values of TabOrder property will render the tabindex attribute. This is the default behavior and it is following HTML best practices. 

Another frequent problem happens when users have a very complex form, composed of multiple frames, all created at run time. It is hard to determine exactly what the user expects to see regarding tab orders. In IW 15.2 a new mechanism was introduced to set the tab orders. When frames are parented to an IWForm, all controls within that frame keep their relative tab orders. All controls with TabOrder values greater than or equal to zero will keep the same value, and controls with positive TabOrder values will receive a new “real” TabOrder (i.e. the actual value that tabindex attribute will have in the final HTML page). The new “real” TabOrder value depends on the TabOrder value of the frame’s parent container in the IWForm. Remember that when creating a frame at run time you also specify the frame’s parent container – it may be the IWForm itself, but most of the time there is another type of container, usually a region. 

An example: you create Frame1 at runtime which contains IWEdit1, IWButton1, and IWButton2.

  • IWEdit1.TabOrder = 1, IWButton1.TabOrder = 2 and IWButton2.TabOrder = 0.

Then Frame1 is parented to a container in the Form, say, IWRegion1. In our example, let’s say IWRegion1.TabOrder is 5. Then, in the final HTML we will have:

  • IWEdit1.TabOrder = 6, IWButton1.TabOrder = 7.

This means that IWEdit1 will be focused right after the predecessor of IWRegion1 (the parent container of the Frame) in the IWForm’s tab order list. Please notice that depending on the value of IWForm.TabOrderThreshold, IWButton1 won’t render the tabindex attribute at all, and the browser will decide the tab order based on its position, as always. 

Besides that, IntraWeb 15.2 also introduced the new TabOrder Wizard which makes it much easier to organize the tab order of multiple controls at once, visually. The wizard can be started from the context menu (right-click on any IW control or form):

Users can move controls using drag and drop (containers – e.g. IWRegion – move all their child controls as well), or alternatively using the Move Up/Move Down buttons. Order can be also set automatically for all displayed controls using Auto Set YX and Auto Set XY buttons, which set the tab order of all controls based on their position within the form, as is during design time.