mheavers
mheavers

Reputation: 30178

accessibility for image inside an anchor link with role button: Clickable elements must be focusable and should have interactive semantics

I have the following markup:

<li class="remove-filter-key-list-item" data-id="{{this.id}}">
  <a
    href="#"
    class="remove-filter-key-button flx jst-cntr"
    role="button"
    tabindex=0
  >
    <img
      class="remove-filter-key-list-icon"
      src="{{this.icon}}"
      alt="{{this.statusText}} icon"
    />
    <span
      class="remove-filter-key-list-title"
    >{{this.statusText}}</span>
  </a>
</li>

which is triggering this error in the firefox accessibility inspector:

enter image description here

From what I can gather, the link needs an onclick listener, a keydown listener, a tab index, and aprevent default on both events. I've tried adding those, but the error persists. Firefox seems to be focused on the image inside the anchor link, not the link itself, which is the clickable thing.

I'm adding the listeners in a javascript file (I do not and can't add them inline in the html) as follows:

function addStatusFilterListener(el) {
  el.addEventListener("click", onStatusFilterToggle);
  el.addEventListener("keydown", (e) => {
    e.preventDefault();
    if (e.keycode === 32) {
      //if spacebar
      onStatusFilterToggle(e);
    }
  });
}

This doesn't seem to work though - the spacebar scrolls the page, and my event is not triggered, and it seems the accessibility inspector is looking for events on the contained img tag. What's the proper way to structure this to resolve these issues?

Upvotes: 2

Views: 1884

Answers (2)

GrahamTheDev
GrahamTheDev

Reputation: 24905

To save a lot of workarounds and fiddles the most straight forward solution is to wrap the image in a button rather than trying to make an anchor behave like a button.

This means that you don't have to worry about different handlers etc. as a click event handler on a button has all the behaviour built in!

In the following example I changed your code to use a <button> instead and added a data-id just to alert to which item is clicked.

You will see that you can use Enter or Space to activate the item as well as a mouse click!

const removeBtn = document.querySelectorAll('.remove-filter-key-button');

removeBtn.forEach((element)  => {
   element.addEventListener('click', onStatusFilterToggle);
});

function onStatusFilterToggle(e){
  let el = e.currentTarget;  
  let itemID = el.getAttribute('data-id');
  alert("you pressed item: " + itemID);
}
button{
   border: 0px;
   background: transparent;
}
button:hover{
   cursor: pointer;
   opacity: 0.9;
}
<button class="remove-filter-key-button flx jst-cntr" data-id="1">
    <img
      class="remove-filter-key-list-icon"
      src="https://picsum.photos/100/100"
      alt="Some Alt Text icon"
    /><br>
    <span
      class="remove-filter-key-list-title"
    >Current Status</span>
  </button>
  <button class="remove-filter-key-button flx jst-cntr"  data-id="2">
    <img
      class="remove-filter-key-list-icon"
      src="https://picsum.photos/100/100"
      alt="Some Alt Text icon"
    /><br>
    <span
      class="remove-filter-key-list-title"
    >Current Status</span>
  </button>

Upvotes: 2

QuentinC
QuentinC

Reputation: 14837

Here are what I could say about your code:

  • tabindex=0 is unnecessary, as a link is focusable by default.
  • keyboard handler is unnecessary, since pressing enter already triggers the link and/or the click handler. It may even be dangerous because you are making the interface unusual against what's expected by the user for a link or button.
  • Adding tabindex=0 to the <img/>, the <span>, or anything inside the link is going to create troubles; nested focusable or clickable elements create confusion because they aren't well understood by users and the actual events triggered isn't well defined and/or hard to handle well.
  • The image has an alt text, so accessibility speaking, you seem all good

Upvotes: 1

Related Questions