Setting the Tab Order for Fields in a Form

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>
Note

Note

Remember, for this page to display account data, the ID of a valid account record must be specified as a query parameter in the URL for the page. For example:

https://Salesforce_instance/apex/myPage?id=001x000xxx3Jsxb
Displaying Field Values with Visualforce has more information about retrieving the ID of a record.

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:

  • <apex:commandButton>
  • <apex:commandLink>
  • <apex:inputCheckbox>
  • <apex:inputField>
  • <apex:inputFile>
  • <apex:inputSecret>
  • <apex:inputText>
  • <apex:inputTextarea>
  • <apex:outputLabel>
  • <apex:outputLink>
  • <apex:selectCheckboxes>
  • <apex:selectList>
  • <apex:selectRadio>

Setting the tabIndex on Components Inside an Iteration

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;
        }
    }
}
Notice the following:
  • The inner class OppWrapper combines a reference to an opportunity with an index number.
  • The getOpportunitesWithIndex method creates a list of OppWrappers, calculating the tabIndex position for each one.
Previous
Next