Skip to content

bug(aria): pointer events on SVG children are discarded by listbox, tabs and tree #33586

Description

@romain-rossi

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

Not a regression. @angular/aria is new in v22; the guard is present in every version I
checked (22.0.6 and 22.1.0-rc.0).

Description

@angular/aria discards pointer events whose target is an SVGElement. Clicking an inline
<svg> rendered inside an option, a tab or a tree item does nothing, while clicking a few
pixels away, on the padding around it, works as expected.

This affects any icon library that renders SVG rather than an icon font (FontAwesome, Lucide,
Material Symbols in SVG mode) as well as hand-written inline <svg>. It is most visible when
the icon is the control: an icon-only option fills its own hit area, so most of the
clickable surface is dead.

Cause. The three _getItem helpers narrow the event target to HTMLElement before
calling .closest():

// src/aria/private/listbox/listbox.ts:295private_getItem(e: PointerEvent){if(!(e.targetinstanceofHTMLElement)){return;}constelement=e.target.closest('[role="option"]');returnthis.inputs.items().find(i=>i.element()===element);}

An inline <svg> and its children are SVGElement, which extends Element but not
HTMLElement, so the guard returns early and the event is dropped before the ancestor lookup
ever runs. .closest() is defined on Element, so the guard is narrower than what the code
below it needs.

Suggested fix. Widen the guard to Element at the three call sites:

FileLine
src/aria/private/listbox/listbox.ts295
src/aria/private/tabs/tabs.ts299
src/aria/private/tree/tree.ts482
- if (!(e.target instanceof HTMLElement)) {+ if (!(e.target instanceof Element)) {
return;
}

Workaround. Make the icon transparent to the pointer so the event retargets to the row:

[role='option'] svg,
[role='tab'] svg,
[role='treeitem'] svg {
pointer-events: none;
}

Reproduction

StackBlitz link: https://stackblitz.com/edit/components-issue-starter-no8g4qz3

The demo renders two identical listboxes side by side; the only difference is the element
type of the option's child. Both start with one selected.

Steps to reproduce:

  1. In listbox 1 (SVG), click the middle of the second circle. Nothing happens, the
    selection stays one.
  2. In listbox 2 (HTML <span>), click the middle of the second disc. The selection
    becomes two.
  3. Back in listbox 1, click the padding of the third option, a few pixels outside the
    <svg>. The selection becomes three.

Step 3 is the clearest: the same option responds or not depending on whether the clicked
pixel belongs to the <svg> or to the <li>.

The listbox markup is:

<ulngListbox[(value)]="selection"><lingOption[value]="'a'"><svgwidth="24" height="24" viewBox="0 0 24 24"><circlecx="12" cy="12" r="10" /></svg></li></ul>

Only the listbox is reproduced in the StackBlitz. Tabs and tree carry the identical guard in
their own _getItem (see the Description above), so the same defect is expected there, but I
have not verified it.

Expected Behavior

Clicking anywhere inside an option, a tab or a tree item selects it, including on an SVG child.
An icon is a decorative child of the row, not a hole in it.

Actual Behavior

Clicking on the SVG does nothing. The event is discarded by the instanceof HTMLElement guard
in _getItem before the option is resolved. Clicking on the surrounding padding of the same
option selects it normally.

Environment

  • Angular: 22.0.8
  • CDK/Material: @angular/cdk 22.0.6, @angular/aria 22.0.6 (guard unchanged in 22.1.0-rc.0)
  • Browser(s): Chrome 150.0.7871.184 (not browser-specific, SVGElement never extends HTMLElement)
  • Operating System (e.g. Windows, macOS, Ubuntu): Windows 11

Metadata

Metadata

Assignees

Labels

P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: manyArea label for issues related to many components

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions