Is this a regression?
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:
| File | Line |
|---|
src/aria/private/listbox/listbox.ts | 295 |
src/aria/private/tabs/tabs.ts | 299 |
src/aria/private/tree/tree.ts | 482 |
- 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:
- In listbox 1 (SVG), click the middle of the second circle. Nothing happens, the
selection stays one. - In listbox 2 (HTML
<span>), click the middle of the second disc. The selection
becomes two. - 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
Is this a regression?
The previous version in which this bug was not present was
Not a regression.
@angular/ariais new in v22; the guard is present in every version Ichecked (22.0.6 and 22.1.0-rc.0).
Description
@angular/ariadiscards pointer events whose target is anSVGElement. Clicking an inline<svg>rendered inside an option, a tab or a tree item does nothing, while clicking a fewpixels 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 whenthe icon is the control: an icon-only option fills its own hit area, so most of the
clickable surface is dead.
Cause. The three
_getItemhelpers narrow the event target toHTMLElementbeforecalling
.closest():An inline
<svg>and its children areSVGElement, which extendsElementbut notHTMLElement, so the guard returns early and the event is dropped before the ancestor lookupever runs.
.closest()is defined onElement, so the guard is narrower than what the codebelow it needs.
Suggested fix. Widen the guard to
Elementat the three call sites:src/aria/private/listbox/listbox.tssrc/aria/private/tabs/tabs.tssrc/aria/private/tree/tree.tsWorkaround. Make the icon transparent to the pointer so the event retargets to the row:
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
oneselected.Steps to reproduce:
selection stays
one.<span>), click the middle of the second disc. The selectionbecomes
two.<svg>. The selection becomesthree.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:
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 Ihave 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 HTMLElementguardin
_getItembefore the option is resolved. Clicking on the surrounding padding of the sameoption selects it normally.
Environment
@angular/cdk22.0.6,@angular/aria22.0.6 (guard unchanged in 22.1.0-rc.0)SVGElementnever extendsHTMLElement)