htmlaccessibilitywai-ariawcag

Is a role="group" valid under a role="tree"


Lets say I have an element with role="tree". Under that I have an element role="group" which contains elements with role="treeitem" or role="group" for multilevel tree.

<... role="tree">
    <... role="group">
        <... role="treeitem">
        <... role="treeitem">
        <... role="group">
            <... role="treeitem">
            <... role="treeitem">

The issue here is that that the accessibility test tool is complaining that the element with role="tree" must contain a child element with role="treeitem".

Looked up the spec and it says

Required Owned Elements:    
        group → treeitem
        treeitem

How do I interpret this? Should a tree contain a treeitem directly? could tree have a group which contains tree items but the tree has no direct children with role "treeitem"?

Here is the complete HTML:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Test Tree A11y</title>
    </head>
    <body>
        <div role="main">
            <!-- tree contains a group which contains treeitems/group -> treeitems -->
            <ul role="tree">
                <ul role="group">
                    <li role="treeitem">Banana</li>
                    <li role="treeitem">Mango</li>
                    <ul role="group">
                        <li role="treeitem">Banana-2</li>
                        <li role="treeitem">Mango-2</li>
                        <li role="treeitem">Orange-2</li>                    
                    </ul>
                </ul>
            </ul>

            <!-- tree contains a treeitems or groups which contains treeitem -->            
            <ul role="tree">
                <li role="treeitem">Banana</li>
                <li role="treeitem">Mango</li>
                <ul role="group">
                    <li role="treeitem">Banana-2</li>
                    <li role="treeitem">Mango-2</li>
                    <li role="treeitem">Orange-2</li>                    
                </ul>
            </ul>
        </div>
    </body>
</html>

Should an element with a role "tree" must contain one or more "treeitems"? Can it contain a "group" which contains "treeitems?"

In the first tree

In the second tree

In the spec, I am not able to find a definite statement that tree must conatin 1 or more DIRECT children with a role treeitem. So I am not certain that the first tree has an actual accessibility issue and if the first tree is violating the spec.

FWIW, there is a control called JSTree which is using the style as shown in first tree, and I am debating if that is a real issue or not.


Solution

  • Short Answer

    Although it is valid HTML and WAI-ARIA to have a group as the direct descendant of a tree it is not the intended or recommended use if it not associated with a corresponding treeitem.

    It is an allowed child of role="tree" due to different ways a tree can be constructed.

    Long Answer

    Firstly it is perfectly "valid" (it is valid markup) to have a role="group" as the only descendant of a role="tree".

    However although valid it is not the recommended way of doing things and may result in unexpected behaviour depending on the screen reader and browser combination in use.

    The key point is that in the WAI-ARIA best practices document it states that:

    Each root node is contained in the element with role tree or referenced by an aria-owns property set on the tree element.

    So the second you put the top level nodes into a role="group" you break this rule unless you give each of the treeitems in your group an id and then use aria-owns on the role="tree". At which point the group becomes pointless anyway as you have bypassed it.

    So why allow a role="group" as a descendant of role="tree" if it is not recommended?

    Now you could argue that if it wasn't valid to have a group as a top-level item in a role="tree" that it shouldn't be allowed in the first place.

    However, the reason that role="group" is allowed at the top level is that you can set a treeitem to open a group and these can be on the same level (siblings).

    I think I can explain this best with an example:-

    Code Example 1 - valid as the group has a parent treeitem and so does not act as a root node.

    <div id="tree1" role="tree" tabindex="-1">
      <div role="treeitem" aria-owns="pizzaGroup" aria-expanded="false" tabindex="0">Pizza Toppings</div>
      <ul role="group" id="pizzaGroup">
          <li role="treeitem">Cheese</li>
          <li role="treeitem">Pepperoni</li>
          <li role="treeitem">Onion</li>
       </ul>
    </div>
    

    In the example above the first div acts as the treeitem and the group is controlled by it. The association is made with aria-owns.

    The reason it states that it must contain group -> treeitem is because an empty group is not allowed.

    The above is a valid example of a role="tree", however I would not recommend that pattern if you can avoid it as support for aria-owns is not great.

    The accepted practice is to have the group nested within the treeitem itself and to use <ul> and <li> throughout. Most screen readers will automatically associate the child nodes with the parent node and those that don't still have a way for their users to workout the relationship as nested <ul> and <li> are well supported.

    Example 2 - an example of the recommended pattern using <ul> and <li>

    <ul role="tree">
        <li role="treeitem" aria-expanded="false" tabindex="0">
        <span>Pizza Toppings</span>
            <ul role="group">
                <li role="treeitem">Cheese</li>
                <li role="treeitem">Pepperoni</li>
                <li role="treeitem">Onion</li>
            </ul>
        </li>
    </ul>
    

    Hopefully that answers why the accessibility tool was complaining about not having a treeitem and why you can have a group as a child of a tree but only if it is controlled by an associated treeitem.

    Final Thoughts

    In your example you haven't provided a label for your tree. Don't forget to add a label to the main element eith role="tree", either a visible label and aria-labelledby="yourLabelID" (preferable) or with an aria-label="description of tree".

    Update after discussions in chat

    If only guidance was more clear!

    As pointed out my supporting information for a "root node" is still not clear and it is fair to comment it could still interpreted that a group can be a root node from that definition.

    However further up in the definition of a treeview, coupled with another of the rules, there is enough clarity to confirm that it is in fact impossible for a group to be counted as a root node at all!

    These are the definitions of nodes in a tree:

    Node An item in a tree.

    Root Node Node at the base of the tree; it may have one or more child nodes but does not have a parent node.

    Child Node Node that has a parent; any node that is not a root node is a child node.

    Parent Node Node with one or more child nodes. It can be open (expanded) or closed (collapsed).

    Take the following rule:

    Each child node is contained in or owned by an element with role group that is contained in or owned by the node that serves as the parent of that child.

    You therefore cannot count a group as a root node (or any node) under any circumstances, it cannot have a parent and so contravenes the above rule.

    Let me explain why this is the case and why you can never treat a group as a root node:

    The second you add a treeitem to a group the following must be true:

    Example 3 - not valid as the group has no parent node and would have to act as a root node (which it cannot).

    <div role="tree">
         <!--this group is a root level node and does not have a parent node. -->
         <div role="group">
             <!--This treeitem must be within a "group" as it is a child node. -->
             <!--This means that the "group" above contains it and that means the group above must be owned or controlled by something.-->
             <!--This treeitem can never have a parent as root level nodes cannot have a parent node.-->
             <div role="treeitem">item</div>
    
         </div>
     </div>
    

    Also the following takes all of the ambiguity away as far as I am concerned:

    Each element serving as a tree node has role treeitem.

    So my original premise was correct for why a group is allowed as a top level item in a tree.

    In fact it is even more clear, a group can never be a root node (or a node at all as you cant have role="treeitem group"!) and the only reason a group can appear as a direct descendant of a tree is if it is a sibling with a treeitem that controls it (it's parent) as I originally said.