4

Disclaimer: I understand that it is not valid HTML. I am trying to understand why is it not allowed?

W3C suggests that an interactive element like button or a mustn't contain another interactive element.

I could find a lot of resources mentioning this rule and some workarounds, also some resources related to how this impacts accessibility and screenreaders, but almost all of those resources talk about the fact that this is a requirement but do not explain why.

https://adrianroselli.com/2016/12/be-wary-of-nesting-roles.html

https://codepen.io/vloux/pen/wXGyOv

Nesting <a> inside <button> doesn't work in Firefox

https://github.com/dequelabs/axe-core/issues/601

I wasn't really able to find an explanation for why is it not allowed? does it lead to any usability problems?

This is a related question: Why should interactive element not be used within an anchor?

The accepted answer is satisfactory but is not enough to make this rule a requirement. The described situation can be avoided using proper event handling.

Also, if nested interactive content is invalid, how are we supposed to have something like this:

A card which is clickable as a whole, and also has a clickable secondary CTA inside it. I know a workaround would be to have a primary and secondary CTA inside the card, but shouldn't the above be allowed as well?

Here is a fiddle: https://jsfiddle.net/t9qbwas5/2/

<button type="button" class="card">
  The card itself is the primary CTA.
  <br/>
  <br/>
  some important content to read through.
  some important content to read through.
  some important content to read through.
  <div class="cta">
   Secondary CTA
  </div>
</button>

.cta {
  padding: 4px;
  background: #00a9a9;
  color: white;
  width: 80px;
  margin: auto;
  margin-top: 8px;
  margin-bottom: 8px;
}

.card {
  width: 200px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
}

enter image description here In the above example, I am achieving this by using a clickable div inside the button, but that is not semantic (?) and also functionality wise, it is an interactive element inside another one. I am trying to understand that even if I use this workaround, is it fundamentally wrong to have nested interactive elements? is it a bad design/usability practice?

gaurav5430
  • 12,934
  • 6
  • 54
  • 111

4 Answers4

4

The answer is actually quite simple in principle. When you click on the interactive element inside another interactive element which function should you trigger?

In your example if I click on Secondary CTA should it fire the function for secondary CTA or should it fire the function for the card?

The fiddle below should demonstrate the problem, tab into the first button and press enter, then tab into the CTA and press Enter.

Obviously you could work around this but I think it demonstates the point.

$('.card').on('click', function(){
    console.log("card");
});
$('.cta').on('click', function(){
    console.log("cta");
});
.cta {
  padding: 4px;
  background: #00a9a9;
  color: white;
  width: 80px;
  margin: auto;
  margin-top: 8px;
  margin-bottom: 8px;
}

.card {
  width: 200px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" class="card">
  The card itself is the primary CTA.
  <br/>
  <br/>
  some important content to read through.
  some important content to read through.
  some important content to read through.
  <div class="cta" tabindex="0">
   Secondary CTA
  </div>
</button>

This principle then continues through to Screen Readers and other Augmentative and alternative communication (AAC) devices.

Should they take into account the parent element when describing the child element? Should they allow the use of Space to activate if you nest a checkbox within a <button>, should Enter then affect only the button or both?

GrahamTheDev
  • 22,724
  • 2
  • 32
  • 64
  • I can see how the above-mentioned points are a problem, but I can also see that the available workarounds are not hackish. if we just change the rule to allow nested interactive content, and leave it to the developer to implement the current behaviour, would it still be impossible to implement this change in ATs behaviour? With the above examples it looks like it should be a best practice to not do so, but not enough reasons for it to be a MUST rule – gaurav5430 May 29 '20 at 19:09
  • 2
    No it wouldn't be impossible. However there are hundreds of things screen readers struggle with already so adding something as complex as this (for example how to handle a hyperlink within a button within a div with a click handler) would be horrendous. You have to bear in mind that valid HTML is a best practice also, if you tried the above example, tested it with every screen reader and browser combination and it was clear how it worked it wouldn't be a problem that the HTML isn't valid. It just helps a lot to make your HTML valid when dealing with accessibility. – GrahamTheDev May 29 '20 at 19:26
  • Look at the w3's ARIA widget roles - they've given very much consideration to various patterns of 'nested interaction' operating within the spec, including a predictable set of keyboard operations in such cases. Latest version of the docs at time of writing is [here](https://www.w3.org/TR/wai-aria-practices-1.1/) - it's a very interesting read. – brennanyoung Jun 09 '20 at 15:15
  • @brennanyoung sorry, missed this comment. can you point to some sections of the doc that can help with this understanding – gaurav5430 Sep 12 '21 at 14:13
  • 1
    try https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_general_within – brennanyoung Sep 15 '21 at 08:55
3

A card which is clickable as a whole, and also has a clickable secondary CTA inside it.

Although visually imaginable and technically possible, it's not accessible for assistive technologies, like screenreaders

Let's make a simple example:

<button>
    Click for action 1
    <button>Click for action 2</button>
</button>

The accessible name for the first <button> would be "Click for action1 Click for action 2". And if you define an aria-label="Click for action 1", then the inner button element would not be read at all.

If you really want to make a whole element clickable, you can perfectly use javascript and still be accessible

<div class="outer">
  <button type="button" class="card">
    The card itself is the primary CTA.
  </button>
  <br/>
  <br/>
  some important content to read through.
  some important content to read through.
  some important content to read through.

  <button class="cta">
   Secondary CTA
  </button>
</div>

<script>
$(".outer").on("click", function() {$(".card").click()});
</script>

<style>
.outer {cursor: pointer}
</style>

With this example, you will correctly have two buttons rendered to screenreaders, the first one "The card itselfis the primary CTA" and the second one "Secondary CTA", while a mouse click on the whole card would lead the same action as the first button.

Adam
  • 17,838
  • 32
  • 54
  • 1
    I get that there are multiple valid workarounds, but fundamentally, design wise we still have nested interactive elements, which could be allowed by default by HTML if the workarounds are valid, and if there is no fundamental foreseeable issue with nesting interactive elements – gaurav5430 May 29 '20 at 20:06
  • I mean just because we are using a div instead of a button, is it suddenly okay for the screenreaders and the html validator? Or are we cheating and doing something that the standard does not want us to do at all – gaurav5430 May 29 '20 at 20:07
  • 1
    We are not cheating as we are offering an accessible way via standard buttons that will be understood and correctly handled by assistive technologies like screenreaders. The onclick event is another and supplementary way for mouse users to achieve the same action as the default button (and transparent to screenreader users) – Adam May 29 '20 at 20:34
  • Actually yes, in your example it makes sense, but then this only works for click, the card would not have the focus/hover/disabled and other interactions that a button has, unless we figure out a way to pass that on to the button, or write some code to make it look like the button in those states, which is not impossible but then it becomes messy – gaurav5430 May 30 '20 at 04:24
  • 1
    @gaurav5430 Keyboard users will use the two buttons, well-identified, so it works perfectly for them. You can provide `.outer:focus` CSS class for mouse users. I don't see anything difficult. Your keyboard focus on the first button should be on the button not on the full outer div. So that's what we want for accessibility – Adam May 30 '20 at 08:19
  • actually I think I understand this answer better now. use html and css for providing the base behaviour for everyone and then add mouse specific behaviour on top using javascript or css – gaurav5430 Sep 12 '21 at 15:09
1

One important problem is related to event-capturing; if you click on a interactive element nested inside another interactive element(e.g. a select inside a clickable button) there would be an interference here and two case might happen depends on the browser;

case 1 is that both element will raise that event (e.g. click) event  

case 2 is that parent element will capture the event and the nested element won't raise that event

in fact both cases will result in non-deterministic behavior;

This is not limited to click events actually, but click event is more tangible; Also the screen reader will fail to parse the markup; keyboard-interaction won't work as expected; try it in the snippet below:

del.addEventListener('click', function(){
  console.log('deleting ...')
})

save.addEventListener('click', function(){
  console.log('saving ...')
})

sel.addEventListener('change', function(){
  console.log('changing to', sel.value)
})
<div id='del'>
delete 
<button id='save'> save 
<select id='sel'>
  <option>foo</option>
  <option>bar</option>
<select>
<input name='a' type='radio' />
<input name='a' type='radio' />
<input name='a' type='radio' />

</button>
</div>
Mechanic
  • 5,015
  • 4
  • 15
  • 38
  • Screen reader would fail to parse the markup, would it be because they have been written keeping this rule in mind, or they would fail to parse the markup even if this rule didn't exist at all? – gaurav5430 May 29 '20 at 19:38
  • What would be the problem with keyboard navigation, once we set a standard for how nested interactive elements would behave. Like, currently if you have nested elements, the focus shifts to the parent and in the next tab it shifts to the nested child, isn't this standard enough? – gaurav5430 May 29 '20 at 19:40
  • @gaurav5430 keyboard navigation wasn't obvious in `select` inside `button` case; I'd added `radio` for a better demonstration; and for your first question; I don't know the exact answer but I guess it fail because of improper structure not because it violates the rule; you can actually test it on-demand by running voice-over when snippet is open and see how it parses this – Mechanic May 29 '20 at 19:59
  • I guess screen reader might fail because it is not expecting this rule to be violated, if tomorrow we remove this rule, the new screen readers might not have any problems. I am trying to understand If there are any problems which can not be handled at all if this rule was relaxed, in which case this rule can remain as strict as it is, but otherwise it can be a SHOULD rule instead of a MUST rule – gaurav5430 May 29 '20 at 20:03
  • It's not so much that the problems wouldn't be solvable, it's more that solving the problems becomes much more complex, time-consuming and much harder to make interoperable across a broad range of user agents (i.e. browsers) and assistive technologies if this kind of thing is 'legal'. Making a UI predictable and interoperable within the spec is hard enough. It's a MUST rule so as to reduce the failure space to reasonable limits. – brennanyoung Jun 09 '20 at 15:10
1

It's hard to answer "why" questions, because there are many factors to be considered and ultimately the reason is that the specification specifies it, but I'll give it a try.

When this behavior was spec'ed, this design style was not very common. A link was normally either a single image or a small portion of text. Take a look at this link to an article from the year 2000:
]
Only the title and the image are interactive. The rest is simple text.

Even today, this is not so common. Take also look at the Microsoft 365 pricing page:

Note how the card itself is not interactive, only the things inside it. You can see the primary CTA "Buy now" in the form of a button and the secondary CTAs in the form of hyperlinks.


Now, about your example: Is that card really a button? It might be subjective, but for me that's not a button. A button normally appears with a color scheme contrasting with the surrounding page. I would make the card a <div> and the secondary CTA a <button>.

However it might be confusing to users, as the card doesn't seem much interactive to me. Consider adding cursor: pointer to the <div> (beyond all the things necessary for accessibility)`.


I noted you tagged . I think this is not a great idea for people using screen readers, and I think most screen readers would have problems interpreting a button inside a button (if the browser accepted that at all).

I would use the "Microsoft 365 pricing page approach" instead. It's simpler and works well with HTML.

D. Pardal
  • 6,173
  • 1
  • 17
  • 37
  • I understand the example, but I don't think the current standard would/should just be based on the archaic usage, and not change according to the new use cases. Is there another issue that can not be handled if we just allow that rule? – gaurav5430 May 29 '20 at 19:34
  • I would argue most people still think that having a interactive element inside another doesn't make sense. Of course, using `
    ` makes it really hard to provide accessibility.
    – D. Pardal May 29 '20 at 19:55
  • If we use div and are able to provide the required accessibility, wouldn't it still fundamentally be a nested interactive element? Just that now it would be valid HTML. I am trying to question whether in this usage with valid HTML, is it suddenly fine to use nested interactive element – gaurav5430 May 29 '20 at 19:58
  • 1
    I just checked and Firefox accepts a ` – D. Pardal May 29 '20 at 20:21
  • Yeah newer browsers do accept this, but according to html validator it is still invalid html. Yes I agree, if possible i would rather use a button than a div, but either way, we are using nested interactive elements, whether or not we have valid html – gaurav5430 May 30 '20 at 04:20