The problem

Why do we need a group?

A screen reader user tabbing through a form will only hear information programmatically associated with the form field that has focus. When the form field is part of a group, the user needs more context than just the form field label to be able to provide the expected information.

This is particularly true for groups of related checkboxes, radiobuttons and groups of identically labeled form fields. A common example of these are text input fields labeled "address" "city" and "phone number" for "shipping address" and "billing address" when both are presented on the same webpage. The user needs to know whether he is filling in shipping or billing address information (when they are not the same).

What's the solution?

The HTML fieldset/legend construct

Whenever possible, try to use the html construct for grouping form fields, i.e. the fieldset/legend construct.

Example 1 (accessible out of the box) fieldset/legend construct

How many roads must a man walk down? (fieldset legend style)

The fieldset construct has caught some slack because people do not like the default visual presentation.

The presentation can always be modified using CSS, while preserving the programatic relationship that is so important to accessibility.

Also, do keep in mind that visually grouping related form elements is important and helps a lot of people e.g. with cognitive disabilities.

Creating a group using ARIA

People cannot always use the fieldset/legend construct. There may be many reasons for that, e.g. when your webpage needs to be published tomorrow and changing the html tags will affect the visual appearance and prompt a whole other round of QA testing and meetings.

Scripting and ARIA to the rescue.

Example 2 (inaccessible, group label not associated with group).

How many roads must a man walk down? (not accessible)

The ARIA solution.

To create a group, and associate the common label to that group, we need to do the following:

  1. Make sure all the elements to be grouped are owned by, (are descendants of), the same element (usually a div). This element is our group container,
  2. The element that contains the common label text for the group needs a unique id attribute. This element does not have to be located in the group container.
  3. We need to add two ARIA attributes, oneARIA role and ONE ARIA property, to the container for the grouped elements:

Example 3 (example 2 made accessible using JQuery)

We took some JQuery and used it to apply the ARIA attributes detailed in the list above. The html code before JQuery is applied is identical between examples 2 and 3, except we had to change the name and ID attributes on the radiobuttons and labels):

How many roads must a man walk down?

jQuery Code Example

Here is the JQuery code that runs on page load and transforms the inaccessible example 2 into the accessible example 3 (requires the JQuery library)

                                        

                                $(document).ready(function () {
                                    $("div#jsinjected p.Question").attr("id", "Question2");
                                    $("div#jsinjected div.answers").attr("role", "group");
                                    $("div#jsinjected div.answers").attr("aria-labelledby", "Question2");
                                });

                            
                            

Javascript code example

If you prefer to use plain old Javascript, you can, it requires a couple of extra lines of code.

The following Javascript function is equivalent to the JQuery solution, should be run onLoad:


                                function addGroupA11yInfo() {

                                    // select the element that contains all the info.
                                    var x = document.querySelector("div.questionGroup");
                                    // Add a unique ID to the paragraph containing the question.
                                    x.querySelector("p.Question").setAttribute("id", "Question1");

                                    // Selecting the group container element.
                                    var y = x.querySelector("div.answers");

                                    // Mark this container as the group container for the radio buttons and their labels.
                                    y.setAttribute("role", "group");
                                    // Connect the container with the element containing the group label, using the ID we just created.
                                    y.setAttribute("aria-labelledby", "Question1");
}

How do I notice the difference with a screen reader?

You can test the difference yourself by comparing the screen reader experience between the examples on this page. Do not use Voiceover (see note #2 below).

Example 1 and 3 should be near identical, since example 1 uses the html construct.

Examples 2 and 3 provide the transition from inaccessible to accessible experience as described below.

Before the change, when you tab to a radiobutton, in the set you will only hear 3 things (the order may vary depending on browser and screen reader used:

You will hear something along the lines of. Radiobutton not checked 22 or 22 radiobutton not checked. You can try it out for yourself using Example 2 on this page.

Notice how you have no idea what the question is? You can find it with a screen reader, but you have to look, which takes time and can be confusing.

After the change, you will hear 4 things (order depends on screen reader/browser combination:

Notes on screen reader compatibility

1. Some screen readers announce the common label only when moving focus into and out of the group. It actually provides a good user experience, as users do not need to hear the common label for every choice within the group, as long as they know when they enter and exit the group

2. At this time (November, 2015), the latest versions of Voiceover (on OSX and iOS) do not support either the fieldset/legened construct or the group. We can use the aria-describedby property on the radiobutton that receives focus and point it to the id of the common label question, to create an equivalent experience for the Voiceover user, but ultimately it is on Apple to fix this. Needless to say, Voiceover will not communicate the important accessibility fixes described on this page until this bug has been fixed.

Common mistakes

We often come across a label element with a group label associated with a container element for the form fields.

<!-- This is not allowed!! -->
<label for="billingAddressGroup">Billing Address</label>

<div id="billingAddressGroup">
<label for="street">Street and House Number.</label>
<input id="street" type="text" size="50">
<label for="city">City</label>
<input id="city" type="text" size="30">
<label for="state">State (2-letter abbreviation)</label>
<input id="state" type="text" size="2">
</div>

This does not work, and is not valid code.

The Definition of the label tag in the html specification states that a label can only be associated with one form control.

If you run the code snippet above through the NU HTML5 Validator you get the following error message for the label associated with div:

Error: The for attribute of the label element must refer to a non-hidden form control.

If this is the case you are dealing with, make sure to remove the "for" attribute from label tag associated with the entire group. Instead you assign it a Unique ID and then associate it with the group container using aria-labelledby as explained above.