Visualforce forms have a “natural order” for tabbing through the input fields: left-to-right, top-to-bottom. For some forms, this may not be the most efficient or accessible arrangement. You can set the tabIndex attribute on input and other components in your page to change the tab order to anything you'd like. For example:
<apex:page standardController="Account"> <apex:form> <apex:pageBlock title="Edit Account: {!Account.Name}"> <apex:pageBlockSection title="Account Details" columns="1"> <apex:inputField value="{!Account.Name}" tabIndex="4"/> <apex:inputField value="{!Account.Website}" tabIndex="3"/> <apex:inputField value="{!Account.Industry}" tabIndex="2"/> <apex:inputField value="{!Account.AnnualRevenue}" tabIndex="1"/> </apex:pageBlockSection> </apex:pageBlock> </apex:form> </apex:page>
Notice that when you display this page and press TAB, the active field changes in the reverse order than you would normally expect.
The tabIndex attribute should be an integer between 0 and 32767, or an expression which evaluates to an integer value in the same range. The tab order begins with component 0 being the first component selected when a user presses TAB.
The tabIndex attribute can be set on the following Visualforce components:
You can set the tabIndex attribute on a component repeated by an iteration component, for example, inside a <apex:dataTable> or <apex:repeat>, but it requires a little more effort.
The obvious solution, setting the tabIndex via an Apex getter method that automatically increments as it is accessed, will not work. Visualforce caches the results of getter methods, and does not guarantee they will be called for each use on a page. See Order of Execution in a Visualforce Page for more details of how Visualforce getter methods should be implemented.
Instead, you can provide the tabIndex value along with the object or field reference in each element in the collection being iterated over. Here's how this might look in your Visualforce page:
<apex:page controller="OppsController"> <apex:form> <apex:dataTable value="{!OpportunitiesWithIndex}" var="oppWrapped"> <apex:column> <apex:facet name="header">Opportunity</apex:facet> <apex:outputField value="{!oppWrapped.opp.name}"/> </apex:column> <apex:column> <apex:facet name="header">Amount</apex:facet> <apex:inputField value="{!oppWrapped.opp.amount}" tabindex="{!oppWrapped.tabIndex}"/> </apex:column> </apex:dataTable> </apex:form> </apex:page>
The <apex:dataTable> component does not iterate over a list of opportunity records, but a list of objects that wrap an opportunity, referenced as {!oppWrapped.opp}, with its tabIndex, referenced as {!oppWrapped.tabIndex}. Here's a controller that provides this collection:
public class OppsController { // Get a set of Opportunities public ApexPages.StandardSetController setCon { get { if(setCon == null) { setCon = new ApexPages.StandardSetController(Database.getQueryLocator( [SELECT name, type, amount, closedate FROM Opportunity])); setCon.setPageSize(5); } return setCon; } set; } public List<Opportunity> getOpportunities() { return (List<Opportunity>) setCon.getRecords(); } public List<OppWrapper> getOpportunitiesWithIndex() { List<Opportunity> opps = this.getOpportunities(); List<OppWrapper> oppsWrapped = new List<OppWrapper>(); Integer idex = 1; for (Opportunity opp : opps) { oppsWrapped.add(new OppWrapper(opp, idex)); idex++; } return oppsWrapped; } public class OppWrapper { public Opportunity opp { get; set; } public Integer tabIndex { get; set; } public OppWrapper(Opportunity opp, Integer tabIndex) { this.opp = opp; this.tabIndex = tabIndex; } } }