How to work around jsx-a11y/no-static-element-interactions restriction?
Asked Answered
L

10

31

I used the following jsx to construct an entry in a FAQ table:

  <li key={faqKey} className={styles.entry} onClick={handleExpandFn}>
    <div>
      <span className={`icon-next ${questionClassname}`} />
      <span className={styles['question-text']}>{faqEntry.question}</span>
    </div>
    {faqEntry.answer}
  </li>

The idea is that when a user click on the entry, it will toggle the open state of the entry. In the other word, when a user clicks on an open FAQ entry, it will close it. Otherwise it will open it.

However the li tag triggers this eslint violation: Visible, non-interactive elements should not have mouse or keyboard event listeners jsx-a11y/no-static-element-interactions

Unfortunately I don't think there is alternative way to the above html markup. Since it is jsx, it also does not allow override such as // eslint-disable-line jsx-a11y/no-static-element-interactions (The inline comment will be printed out to the web page)

So how I can work around it? Happy to use different jsx markup or jsx eslint override

Lifeblood answered 11/4, 2017 at 5:16 Comment(0)
L
8

One solution I can remember is to use an anchor element with tab-index.

<a style={{display: 'list-item'}} tabIndex={-42} key={faqKey} className={styles.entry} onClick={handleExpandFn}>
  <div>
    <span className={`icon-next ${questionClassname}`} />
    <span className={styles['question-text']}>{faqEntry.question}</span>
  </div>
  {faqEntry.answer}
</a>
Lotte answered 11/4, 2017 at 6:52 Comment(3)
Thx for the answer. What is a sensible tabIndex number? Any integer?Lifeblood
You can probably use the index of the item if the data comes from an array.Lotte
The warning is gone because the onClick attribute is moved from a static/non-interactive element (i.e. <li></li>), to an interactive element (i.e. <a></a>. Here is the W3C reference for interactive html elements.Lotte
A
104

For those who are looking for a workaround, use role="presentation" in your tag.

Arrhythmia answered 22/1, 2020 at 20:43 Comment(2)
Does role="presentation" has negative impact for A11y?Bacillary
yes. If one were to do this, then they would create different issues that need to be resolved.Itacolumite
I
15

Here's how you could revise the markup to be semantically appropriate and get the onclick off the <li>

<li key={faqKey} className={styles.entry}>
  <h3>
    <button type='button' onClick={handleExpandFn}>
      <span className={`icon-next ${questionClassname}`} />
      <span className={styles['question-text']}>{faqEntry.question}</span>
    </button>
  </h3>
  <div className='add_a_panel_class_name_here'>
    {faqEntry.answer}
  </div>
</li>

So with the above: the <h3> will make the titles of the FAQs easily searchable by screen reader users that are navigating by headings

placing the <button> inside of the <h3> gives you the appropriate element to attach a click event to (you want to use a button here because you're toggling state. an <a> should be used if you were going to a new page. You also don't need to add a tabindex to a button as they are inherently focusable).

There are some additional steps you'd likely want to implement, using ARIA expanded and controls attributes on the button, but the above should get you to a good base for moving beyond your error.

Itacolumite answered 18/4, 2017 at 11:24 Comment(1)
Sadly, this is the only right answer here.Barkeeper
N
9

You could put eslint-disable-line in the jsx

  <li // eslint-disable-line jsx-a11y/no-static-element-interactions
    key={faqKey} 
    className={styles.entry} 
    onClick={handleExpandFn}
   >
    <div>
      <span className={`icon-next ${questionClassname}`} />
      <span className={styles['question-text']}>{faqEntry.question}</span>
    </div>
    {faqEntry.answer}
  </li>

Another option, add role='presentation'

  <li 
    key={faqKey} 
    className={styles.entry} 
    onClick={handleExpandFn} 
    role='presentation'
  >
    <div>
      <span className={`icon-next ${questionClassname}`} />
      <span className={styles['question-text']}>{faqEntry.question}</span>
    </div>
    {faqEntry.answer}
  </li>
Nates answered 23/10, 2019 at 23:53 Comment(0)
U
9

To overcome or avoid this error in ES Lint.

You can use three things based on your needs and requirements

aria-hidden="true" - will remove the entire element from the accessibility API
role= "presentation" - The presentation role is used to remove semantic meaning from an element and any of its related child elements.
role= "none" - will remove the semantic meaning of an element while still exposing it to assistive technology. 

There are limitations as well:

  1. Hide content from assistive technology
  2. Cannot be used on a focusable
  3. Element cannot be used on the parent of an interactive element
Utilize answered 21/4, 2021 at 16:19 Comment(0)
L
8

One solution I can remember is to use an anchor element with tab-index.

<a style={{display: 'list-item'}} tabIndex={-42} key={faqKey} className={styles.entry} onClick={handleExpandFn}>
  <div>
    <span className={`icon-next ${questionClassname}`} />
    <span className={styles['question-text']}>{faqEntry.question}</span>
  </div>
  {faqEntry.answer}
</a>
Lotte answered 11/4, 2017 at 6:52 Comment(3)
Thx for the answer. What is a sensible tabIndex number? Any integer?Lifeblood
You can probably use the index of the item if the data comes from an array.Lotte
The warning is gone because the onClick attribute is moved from a static/non-interactive element (i.e. <li></li>), to an interactive element (i.e. <a></a>. Here is the W3C reference for interactive html elements.Lotte
K
6

if you are trying to implement menu using li then the right solution is using role="menuitem" in your li tags. More details about it: https://w3c.github.io/aria/#menuitem

Kalin answered 25/5, 2020 at 12:46 Comment(0)
P
2

use

aria-hidden="true"

on non interactive element tag. like

<span aria-hidden="true" onClick={() => void}>Text</span>
Prevocalic answered 19/10, 2020 at 6:47 Comment(1)
Are you sure this is what you want to do? Prevent screen-readers from letting visually-impaired users know that the element contains "Text"? Adding aria-hidden="true" to an element removes that element and all of its children from the accessibility tree. (source: developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/…)Kurrajong
H
1

You can actually add the eslint override as a comment in JSX. You have to nest the comment inside of braces {}, so that it will be interpreted as JavaScript. Then, of course, since it is a JavaScript comment, it will not be rendered in the JSX.

Depending on your style preference, you can either do it on the line directly before the code

{/* eslint-disable-next-line */}
<li key={faqKey} className={styles.entry} onClick={handleExpandFn}>

or, at the end of the same line

<li key={faqKey} className={styles.entry} onClick={handleExpandFn}> {/* eslint-disable-line */}

Check out the docs for more information on using JavaScript inside of JSX.

Hazelton answered 5/4, 2019 at 21:5 Comment(2)
What is the point of using eslint if you re going to turn off warning this way?Doily
This way only silences a single line's warning(s). ESLint has general rules that work well for the lion share of cases; but, some of those rules are opinionated (and you may have a different opinion). Also, edge-case situations exist. It's better to have the option of using a tool you may not need, than it is to be stuck with that one annoying warning for something that, in your particular case, is not actually an issue. Also, a linter should be an advisor, not a "take it or leave it" dictator. As a programmer, you should have the freedom to make decisions.Hazelton
R
1

Div is a non interactive element and it is only used to show content and containers in user interface. There are few non interactive elements other than <div> like <main>, <area>, all heading tags (like h1, h2, etc.), <p>, <img>, <ul>, <li> and <ol> which does not support or say is not appropriate to use events on it.

So add a role and tabIndex to make the div interactive and tabbable:

 <li key={faqKey} className={styles.entry} role="button" tabIndex={0} onClick={handleExpandFn}>
    <div>
      <span className={`icon-next ${questionClassname}`} />
      <span className={styles['question-text']}>{faqEntry.question}</span>
    </div>
    {faqEntry.answer}
  </li>
Reeher answered 14/5 at 12:4 Comment(0)
L
-1

Instead of rolling my implementation of faq table with collapse/expand interactive feature, I replace it with react-collapsible.

It of course gets rid of the jsx-a11y/no-static-element-interactions as a result.

Lifeblood answered 13/4, 2017 at 11:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.