DEV Community

Cover image for Easy Accessible Click Handlers
Andreas Riedmüller
Andreas Riedmüller

Posted on • Edited on

13 2 1

Easy Accessible Click Handlers

If you add a click event to a non-interactive html element such as a div you should also add keyboard support. For interactive elements like a button this is already taken care of by the browser.

If you use eslint-plugin-jsx-a11y you will see this warning when you add an onClick event:



71:13  warning  Visible, non-interactive elements with click handlers must have at least one keyboard listener jsx-a11y/click-events-have-key-events


Enter fullscreen mode Exit fullscreen mode

To get rid of this warning, my default approach is to add an additional handler for onKeyDown, filter for the enter/space key and trigger the same event as I have for onClick.

Additionally I add the role and tabIndex attributes as recommended.



function handleClick() { setWhatever(true) }

function handleKeyDown(e) {
  if (e.key === "Enter" || e.key === " ") {
    handleClick();
  }
}

return <div
  role={'button'}
  tabIndex={0}
  onClick={handleClick}
  onKeyDown={handleKeyDown}
>Click me!</div>


Enter fullscreen mode Exit fullscreen mode

In comparison to a button this adds quite a lot of code and makes simple components appear more complex than they really are:



function handleClick() { setWhatever(true) }

return <button onClick={handleClick}>Click me!</button>


Enter fullscreen mode Exit fullscreen mode

To avoid this, you can add a simple helper function that returns all required attributes, I called mine accessibleOnClick:



export function filterKeyTriggerButton(handler) {
  return e => {
    if (e.key === "Enter" || e.key === " ") {
      handler(e);
    }
  }
}

export function accessibleOnClick(handler) {
  return {
    role: 'button',
    tabIndex: tabIndex ?? 0,
    onKeyDown: filterKeyTriggerButton(handler),
    onClick: handler
  }
}


Enter fullscreen mode Exit fullscreen mode

TypeScript version:



export function filterKeyTriggerButton(
  handler: (e: React.KeyboardEvent) => void
) {
  return (e: React.KeyboardEvent) => {
    if (e.key === "Enter" || e.key === " ") {
      handler(e);
    }
  };
}

export function accessibleOnClick(
  handler: (e: React.KeyboardEvent | React.MouseEvent) => void,
  tabIndex?: number
) {
  return {
    role: "button",
    tabIndex: tabIndex ?? 0,
    onKeyDown: filterKeyTriggerButton(handler),
    onClick: handler,
  };
}


Enter fullscreen mode Exit fullscreen mode

In your JSX you can now use the spread opperator to add all attributes returned by accessibleOnClick.



function handleClick() { setWhatever(true) }

return <div
{...accessibleOnClick(handleClick)}
>Click me!</div>


Enter fullscreen mode Exit fullscreen mode

This is one of my favorite helper functions and I use it on a regular basis.

Do you have a similar/different approach for this type of click handlers? Let me know!

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more