Skip to content

Latest commit

 

History

History
75 lines (52 loc) · 4.58 KB

File metadata and controls

75 lines (52 loc) · 4.58 KB

ember/template-no-invalid-interactive

💼 This rule is enabled in the 📋 template-lint-migration config.

Disallow non-interactive elements with interactive handlers

Rule Details

This rule prevents adding interactive event handlers (like onclick, onkeydown, etc.) to non-interactive HTML elements without proper ARIA roles.

Examples

Examples of incorrect code for this rule:

<template>
  <div onclick={{this.handleClick}}>Click me</div>
</template>
<template>
  <span onkeydown={{this.handleKey}}>Press key</span>
</template>

Examples of correct code for this rule:

<template>
  <button onclick={{this.handleClick}}>Click me</button>
</template>
<template>
  <div role="button" onclick={{this.handleClick}}>Click me</div>
</template>
<template>
  <button {{on "click" this.handleClick}}>Click me</button>
</template>

Escape hatches

An element opts out of this rule's handler-on-non-interactive check in two cases:

  • aria-hidden="true" (including valueless / empty / {{true}} / {{"true"}} forms) — the author has explicitly removed the element from the accessibility tree, so a "non-interactive element with handler" is moot; AT users won't encounter it either way. Explicit aria-hidden="false" / {{false}} still flags.
  • role="presentation" / role="none" — the author asserts the element is decorative. We accept role="presentation" and role="none" (case-insensitive, first recognized token of a space-separated role list per WAI-ARIA first-recognized-token rule) — a deliberate superset of jsx-a11y's exact-match hasPresentationRole for consistency with WAI-ARIA 1.2 §4.1 role-fallback semantics.

The valueless aria-hidden case (e.g. <div aria-hidden>) is genuinely contested — jsx-a11y, vue-a11y, axe-core, and the WAI-ARIA spec take four different positions on whether it counts as "hidden". This rule leans toward fewer false positives: flagging a handler on an author-decorated element creates friction more often than it catches real bugs.

Related checks outside this rule's scope

  • role="presentation" on focusable elements — per WAI-ARIA 1.2 §4.6 Conflict Resolution, browsers ignore role="presentation" on focusable elements. axe-core's presentation-role-conflict flags this pattern as an authoring error. This rule does not detect the conflict: the escape hatch check runs before isInteractive(node), so a focusable element with role="presentation" returns early and is silently exempted. Interactive-handler cases on plain <button> / <a href> etc. are still flagged normally when they lack the presentation/none opt-out. If you want to catch the authoring error itself (role=presentation on a focusable element, which has no effect at runtime), layer axe-core or a dedicated rule on top.
  • Click handler on a non-focusable decorated element — e.g. <div role="presentation" {{on "click"}}>. Our escape hatch silences this by design (jsx-a11y-compat). axe-core's click-events-have-key-events is the complementary check. If you want strictness, layer it on top.

Options

Name Type Default Description
additionalInteractiveTags string[] [] Extra tag names to treat as interactive.
ignoredTags string[] [] Tag names to skip checking.
ignoreTabindex boolean false If true, tabindex does not make an element interactive.
ignoreUsemap boolean false If true, usemap does not make an element interactive.

References