ARIA controls vs ARIA owns

Posted:
Tagged with:

Introduction

So, as the page title says we're going to discuss the differences between both aria-owns and aria-controls, they're completely different attributes, with completely different purposes so before we dive a little deeper, let's just try to understand where the confusion may be coming from.

As developers trying to build components that can be accessed by users of assistive technologies, sometimes we need to reach for some ARIA, where HTML alone cannot effectively pass all of the necessary information to the accessibility tree. Obviously the best strategy here is to try to avoid or limit the use of ARIA and use good old HTML where we can, but of course, that's not always possible.

So, sometimes we need to use ARIA to create relationships between nodes, be they static nodes or interactive nodes and we may consider doing this for a variety of reasons. A couple of examples could be:

When we need to create those relationships, we need to reach for the right attribute, as randomly grabbing the one that sounds like it might be a good fit, based upon its name alone could in fact just make things worse, in which case, it's likely that the "No ARIA is better than bad ARIA" statement comes into play.

How are both attributes similar?

They both have the word 'aria' in them.

I won't just leave it there, there are some other similarities:

There may well be other similarities, but none that I know of, I'm sure smarter folk than me could furnish you with more, but we'll just roll with what we have, as this article isn't about the similarities, it's about the differences and that's the important distinction.

So, how are they different?

I have thus far never been in a situation where I have added aria-owns to anything, ever, I'm sure there are valid use cases for it, as the folks that create this stuff tend to do so for the greater good and perhaps I've just been fortunate enough to have full access to the HTML, in most cases.

I do tend to use aria-controls though and I use that in basic disclosure patterns, so a button which when interacted with will reveal some previously hidden content. We'll start with this property, as more often than not, this may be the one you actually need.

ARIA Controls

This property is often used on an interactive element and that interactive element controls another element via creating a relationship through its value, which must be a valid ID in the DOM, as an example, let's look at the following mobile nav example:

<button aria-controls="navList" aria-expanded="false">Menu</button>
<ul id="navList">
<li><a href="/somewhere.html">Somewhere</a></li>
<!-- Other items -->
</ul>

The above code example is pretty common, I've omitted wrapping that in a <nav> element as whilst that does create a relationship between all of the elements within, it does not create a controlling relationship on the button and the target element, in this case, the target element would be our list which would have its visibility/display/exposure toggled when a click event occurred on the button.

This property creates a relationship, which I guess in layman's terms means "interacting with me will do something to the target element". These elements do not even need to be close by in the DOM, perhaps your navigation is a side drawer or something and its position in the DOM is not close to the trigger, that relationship is still exposed to the accessibility tree. Of course, when we have such an element, it is typically much better to have the target element somewhat adjacent to the triggering element, as otherwise we'd need to faff around with focus management and reading order, as the newly disclosed content must either be next in the sequential focus order or focus must move to the first interactive element within our target node (assuming it has one), so without managing that focus, we could make discoverability of our newly disclosed content difficult for our users, which is obviously something we must avoid.

Does ARIA Controls have good support?

It is exposed by browsers, in the accessibility tree, but then whether a screen reader for example does anything with that, is a different matter altogether. The A11y Supports website is great for getting a quick overview of these things, but as far as I am aware this is maintained by just one person, Michael and despite the fantastic work Michael does for the community, he is only one person, dedicating a chunk of his free time to this cause, so naturally he cannot possibly keep up with every every change to the ARIA spec, every browser and assistive technology update and of course updates to operating systems etc. There is usually a callout element on the page, which informs you when the attribute was last tested and this may be useful for deciding if you want to rely on Micahel's resource or not. I do believe he would love help from the community, so if you have a full suite of assistive tech and some free time, I'm sure Michael and the community at large would be delighted.

In the table for ARIA Controls on the A11y Support site, it looks like only JAWS does anything with the property but still, that relationship is exposed to the accessibility tree, so should assistive technology vendors decide to implement that as per the intent in the spec, then we already have it. Here's a screenshot of the accessibility inspector in Chrome's DevTools, showing that the property is at least exposed to the accessibility tree:

Screenshot of the button and list we created earlier open in Chrome, the button is the highlighted node in the DevTools and we have the accessibility panel open, which shows that the relationship is understood, as the Controls information references the ID of our list element.

The same information is also available on both Safari and Firefox, both on the latest version of MacOS (13.4.1) and all three browsers are up to date. I've not checked exposure of this on mobile devices or other operating systems, so this is less than comprehensive on my behalf.

Arguably, this property is optional, due to the fact that screen readers don't do a great deal with it and assuming that your trigger and target element are adjacent in the DOM (or have no other content between them), then at the time of writing, I don't believe anybody is going to fail your expanding widget for omitting it, but that being said, I do use it and if I have to fail a disclosure widget for another reason, my code example will include the property as a best practice and I do this as I read a lot of articles from industry experts and when they include it in similar examples, as long as I understand why, I also include it too.

This property is of course not restricted to just mobile navigations, it is also used on accordions, typeahead type searches, custom select inputs and much more. The ARIA 1.2 spec provides the details about the property.

ARIA Owns

This is perhaps a little more complex to understand, which I guess is where the confusion comes in, as this is the property I see misused most often.

In HTML we have nesting, we can wrap an element or elements within others (assuming it is per the spec of course), this creates a tree-like structure, as developers we would typically make our code easier to understand and read by indenting nested elements in our code editor. The DevTools in the browser also help us by displaying indention, even when we compress our HTML.

When we nest elements we have an "owning element" and at least one "owned element", more commonly we refer to these as parents and children, respectively and items at the same level will naturally be siblings. The easiest example I can think of to demonstrate this is a list, for anybody not familiar with HTML, that would be structured like so:

<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>

In our very basic example above, ignoring the fact that the list would definitely have a parent and it would likely have siblings, what we have is a <ul> element that is the parent element (the owning element) and two <li> items which are the children elements (the owned elements) and by extension, both those child elements are siblings.

Why are you telling me this? I know this

The reason I went back to basics above is not to make the assumption that somebody reading this has no prior knowledge of HTML, some readers won't be developers but some will likely understand HTML if they are a tester and some may not. That isn't the primary reason though, the primary reason is actually the relevance it has to aria-owns and I personally find it useful to be able to make the distinction on the fly, so when I'm reading other folks' code, in the DOM and I see aria-owns used, I immediately know whether it was used incorrectly.

OK so what does it do?

Essentially, when we add the aria-owns property to a node and then reference the ID of another, we are in essence telling the accessibility tree that the node with aria-owns on, is the parent of the target node (The referred to ID).

But it looks fine in the DOM

It will look exactly the same in the DOM, as ARIA isn't really for the DOM, it doesn't manipulate it or add functionality etc, so maybe this is less evident as we could be looking in the wrong place? let's take our earlier example of a mobile nav and swap the aria-controls property for the aria-owns property, this is available in the following code example:

<button aria-owns="navList" aria-expanded="false">Menu</button>
<ul id="navList">
<li><a href="/somewhere.html">Somewhere</a></li>
<!-- Other items -->
</ul>

In just code, that perhaps looks quite harmless to many and could perhaps be missed quite easily. Let's see how it looks in the DOM, I'm going to duplicate it and add aria-controls back on the first, I'll change text labels and IDs of course, but I want to this for my comparison:

Firstly let's look at our original, I have just changed the name of the button text in this one:

Screenshot of the DOM with a disclosure button, this is the code we initially used and is also showing the accessibility properties in the accessibility panel, we are just focusing on the fact that the name matches what is visible in the button and also the fact that it correctly references our list element.

We won't go into any detail here, as we did earlier, so let's look into the aria-owns example:

Using the code example we have been using throughout, in our disclosure pattern I have removed aria-controls and added aria-owns, this screenshot shows the DOM in the DevTools and the accessibility pane shows that the name of the button has been appended and there is a reference to the owned element

What did you notice there? Perhaps the first thing you noticed in the screenshot or indeed a browser if you followed along that way is that the Controls property in the accessibility pane has been replaced with the Owns property, which is of course to be expected. Did you notice the accessible name of our button was appended? Top marks if you did, it's being appended with the text from within, as we only have one link in our mock mobile menu, it's simply being appended with the word "elsewhere", so the name of our control becomes "I own something elsewhere", which was perhaps a little unexpected to some of us?

So what is going on?

Well the first thing that is happening is because I did not actually make this navigation interactive, the list itself is always exposed to the accessibility tree. Should I have toggled the visibility of the list with a little CSS and JS, that name would not have been appended, whilst the list was accessibly hidden.

So, Safari and VoiceOver on MacOS/iOS do not support this property at all, so if you are testing with VoiceOver on Mac, you won't hear anything out of the ordinary in Safari, as it is ignored. Oddly, VoiceOver does not ignore it with other browsers on a Mac, so I can at least stay on this device as I'm only attempting to explain the differences.

But, what if we use Firefox or even commit an accessibility faux pas and try it with Chrome with VoiceOver? In both instances the property is communicated, the name is read out on Chrome the same as the accessibility pane indicated it would: "I own something Elsewhere", which is obviously not great at all and would be even worse if there were lots of links in that list.

Firefox and VoiceOver actually gives us a little more information, which may just be the kicker in realising how bad this actually is: "I own something, bullet, elsewhere, collapsed button group" Yuck, right?

Note: I don't usually test VoiceOver in Chrome, but I just wanted to demonstrate how it is exposed on browsers other than Safari.

Ok that's bad, why does it actually do that?

Essentially, by using aria-owns, you're modifying the structure of the accessibility tree to be different than the DOM, by telling the accessibility tree "Put all this stuff inside that button". This is similar to what happens when folks wrap a whole card in a link, it gets read out, along with its contents. It's ultimately telling the accessibility tree to interpret your button as this:

 <button aria-owns="navList2" aria-expanded="false">
I own something
<ul id="navList2">
<li><a href="/elsewhere.html">Elsewhere</a></li>
<!-- Other items -->
</ul>
</button>

Which is obviously not what we would type out in a code editor, as we'd have lots of linters, validators, accessibility tools and people shouting at as, perhaps all at the same time. We would never put a list in a button, as it's not permitted in HTML (block level elements in inline block elements, nope) and we would not nest interactive elements, would we?

As we wouldn't nest interactive elements in our actual HTML, why would we want to do this for the accessibility tree? I can't think of a reason to do this as having a list in a button makes no sense, neither does having links inside buttons.

If I just add a few more links into that list, what we get sounds even more awful:

Screenshot of VoiceOver's text viewer, with the button using aria owns, this time there are three items in the list, the ouput is explained in the next paragraph.

The output of the above VoiceOver text viewer sounds pretty bad, again this is in Firefox and the output is as follows: "I own something, bullet, elsewhere, bullet, elsewhere 2, bullet, elsewhere 3, collapsed, button, group". In the wild, that list would have way more than three links, each of which would append to the accessible name and whilst that list won't always be exposed to the accessibility tree (assuming it hides correctly) the thing is, it's not always hidden. The moment you interact with that button and the list is exposed to the accessibility tree, everything is read out, the trigger's name, role and value and all of the contents of that list, which I doubt any screen reader user would thank you for.

I concede to not being very thorough here, I haven't tested this on NVDA or JAWS and that's not due to me being lazy, I'm not testing support as such, I'm, just explaining how it works or how it is supposed to work. We have accordions on this site, you can find one here on the guides page, if you open the DevTools in anything other than Safari and change the aria-controls property on the button to aria-owns, then pop on your screen reader and expand the accordion, try that across a couple of browsers and see if you get the mess that I got in Firefox and VoiceOver.

We can actually get this information if we open the full accessibility tree in the DevTools:

Screenshot of the DevTools, showing the accessibility tree, both buttons are displayed and the nesting demonstrates that the list is actually nested inside the button when aria owns is used, but not when aria controls is used

We can actually easily find out what aria-owns does to the element in the accessibility tree, in the screenshot, the button with the aria-controls property has the list as a sibling, which is what we want for this type of relationship, but the button with aria-owns has the list nested within the button, which is definitely not what we want.

Are there any use cases for ARIA Owns?

Of course, there are likely many. The folks that work on ARIA are smart, they work hard to get this stuff into the spec and ultimately browsers and assistive tech and that will all be based upon gaps in HTML and much of this information would be based upon what users of assistive technologies have reported to them. So, they don't just create attributes for the sake of creating them, so there are likely lots of valid use cases for this property.

I somewhat struggled to think of a potentially viable use case, but I guess (take this with a pinch of salt), if you had a drawer-type navigation, which wasn't adjacent to the trigger element and you needed it to be exposed as being in the same <nav> landmark as the trigger, maybe that would be a use case for it? I guess that would look something like this (please don't copy this code):

<nav aria-owns="drawer">
<button id="menu" aria-expanded="true" aria-controls="drawer">Menu</button>
</nav>
<main>
<h1>lorem ipsum</h1>
<p>Lorem ipsum, dolor sit amore</p>
</main>
<footer>
<!-- footer stuff -->
</footer>
<div id="drawer">
<a href="/detination.html">The end</a>
</div>

That did appear to have the desired behaviour in both Chrome and Firefox, using VoiceOver, in that using the virtual cursor, I accessed the content of the drawer, before the main landmark and everything sounded as expected.

Essentially what I did here was referenced the ID of the drawer on the <nav>. If we were to construct this from scratch we'd obviously want both the button and the drawer in <nav> element, but we're just pretending that for some hypothetical reason, the drawer is placed at the bottom of the DOM. The ARIA 1.2 spec for ARIA owns states that "If an element has both aria-owns and DOM children then the order of the child elements with respect to the parent/child relationship is the DOM children first, then the elements referenced in aria-owns", which means we don't need to build up the list of IDs in the same we would for aria-labelledby's accessible name, so we just use the one ID (in this example) that is the one for our drawer, we don't need the ID of the actual child button, as we get that for free. So the trigger would come first in the reading order and then the actual drawer's contents, as this behaviour is the expected behaviour.

You may notice I also used aria-controls, but I obviously did not use it on the same element, I used it on the button, as that is of course the controlling element.

I have created this code example as a demonstration, there is no substitute for placing things correctly in the DOM and whilst in my limited testing, the DOM was read in the intended order using cursor keys, the focus order remained in the same order as the DOM order, so that would be an issue if we didn't manage focus correctly.

Also, this property is not supported with VoiceOver and Safari, so anything we would have achieved for other combos, would simply not work for most Mac or all iOS screen reader users (as all browsers on iOS are Safari, right?).

I struggled to think of a use case, as I personally have not had to use this myself and trying to keep my example on topic, with a navigation disclosure, this was the best example I could come up with. The best solution for the above is simply putting the whole drawer in the <nav> and using CSS to position the drawer, absolutely to its next position: relative; parent, which would likely be the <body> element or a suitable wrapping <div> if that's your bag.

Wrapping up

Hopefully it's now clearer what the difference between these two ARIA properties are. When we want to create a relationship that indicates an element (usually an interactive element) controls something else in some way, we should use aria-controls, but on those rare occasions where it is not possible to create or adapt a DOM hierarchy that correctly creates the Owning Element and Owned Element (parent child) relationship, then the intent of of aria-owns would be more suitable, but given it's not supported in the main browser/screen reader combo for users of Macs and not supported on iPads or iPhones at all, it would require some additional JS to get this working and it may just be worth considering using a modal instead.

Also, if you need to explain why an accordion or other trigger for a disclosure widget shouldn't have aria-owns as an attribute, to a developer that felt the need to just lob it on there, for good measure, hopefully there is something in here that will make that task easier for you.

Share on:

TwitterLinkedIn

Site preferences

Please feel free to display our site, your way by finding the preferences that work best for you. We do not track any data or preferences at all, should you select any options in the groups below, we store a small non-identifiable token to your browser's Local Storage, this is required for your preferencesto persist across pages accordion be present on repeat visits. You can remove those tokens if you wish, by simply selecting Unset, from each preference group.

Theming

Theme
Code block theme

Code theme help

Code block themes can be changed independent of the site theme.

  • Default: (Unset) Code blocks will have the same theme as the site theme.
  • Light 1: will be default for users viewing the light theme, this maintains the minimum 7:1 (WCAG Level AAA) contrast ratio we have used throughout the site, it can be quite difficult to identify the differences in colour between various syntax types, due to the similarities in colour at that contrast ratio
  • Light 2: drops the contrast for syntax highlighting down to WCAG Level AA standards (greater than 4.5:1)
  • Dark: Syntax highlighting has a minimum contrast of 7:1 and due to the dark background differences in colour may appear much more perceivable

Motion

Motion & animation

Motion & animation help

  • Default (Unset): Obeys device settings, if present. If no preference is set, there are subtle animations on this site which will be shown. If you have opted for reduce motion, smooth scrolling as well as expanding and collapsing animations will no longer be present, fading transtitions and micro animations will still be still present.
  • None: All animations and transitions are completely removed, including fade transitions.

Links

Underline all links

Underline all links help

  • Default (Unset): Most links are underlined, with a few exceptions such as: the top level links in the main navigation (on large screens), cards, tags and icon links.
  • Yes: Will add underlines to the exceptions outlined above, resulting in every link being underlined

Text and paragraphs

Font size (main content)

Font size help

This setting does not apply to the site's header or footer regions

  • Default (Unset): Font sizes are set to site defaults
  • Selecting Large or Largest will increase the font size of the main content, the size of the increase depends on various factors such as your display size and/or zoom level. The easiest way to determine which option suits you best would be to view this text after clicking either size's button
Letter spacing

Letter spacing help

  • Default (Unset): Default letter spacing applies
  • Increased: Multiplies the font size by 0.12 and adds the sum as spacing between each character
Line height

Line height help

  • Default (Unset): all text has a minimum line height of 1.5 times the size of the text
  • Increased: all text has a line height of twice the size of the text
Paragraph spacing

Paragraph spacing help

Paragraph spacing help
  • Default (Unset): The space between paragraphs is equivalent to 1.5 times the height of the paragraph's text
  • Increased: The space between paragraphs is equivalent to 2.25 times the height of the paragraph's text
Word spacing preference

Word spacing help

  • Default (Unset): No modifications to word spacing are present
  • Increased: Spaces between words are equivalent to 0.16 times the font size