-
Notifications
You must be signed in to change notification settings - Fork 839
fix(idref): fallback to qsa #4844
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
@@ -12,8 +12,8 @@ import { getNodeFromTree } from '../../core/utils'; | |||
* @return {string} | |||
*/ | |||
function accessibleText(element, context) { | |||
const virtualNode = getNodeFromTree(element); // throws an exception on purpose if axe._tree not correct |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is not true as getNodeFromTree
does not throw, just returns null
if the node isn't found.
} catch { | ||
throw new TypeError('Cannot resolve id references for non-DOM nodes'); | ||
const rootVNodes = getRootVNodes(vNode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is closer than previous attempts, but I think it's going to do the wrong thing with slotted elements; those appear in the virtual tree under the shadowId of the thing they're slotted into, but for idrefs purposes their id counts as being scoped to the root in which they're defined, not the one they're slotted into:
<!-- Behavior is consistent in current chrome/firefox/safari -->
<label for="slotted-input-a">out-of-shadow label</label>
<my-custom-elm>
<template shadowrootmode="open">
<label for="slotted-input-a">in-shadow label competing with out-of-shadow label</label>
<label for="slotted-input-b">in-shadow-only label</label>
<slot></slot>
</template>
<!-- Accessible name is "out-of-shadow label" -->
<input id="slotted-input-a"></input>
<!-- Accessible name is empty -->
<input id="slotted-input-b"></input>
</my-custom-elm>
I'm not sure there's enough information in the virtual tree right now to distinguish that case correctly; it might be necessary for us to start tracking information about the "source shadow ID" or something for slotted elements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When thinking what to track, consider also this test case:
<label for="slotted-input">out-of-shadow label</label>
<level-a-custom-elm>
<template shadowrootmode="open">
<label for="slotted-input">shadow level A label</label>
<level-b-custom-elm>
<template shadowrootmode="open">
<label for="slotted-input">shadow level B label</label>
<slot></slot>
</template>
<slot></slot>
</level-b-custom-elm>
</template>
<!-- Accessible name is "out-of-shadow label" -->
<input id="slotted-input"></input>
</level-a-custom-elm>
* @param {Element|VirtualNode} node | ||
* @returns {VirtualNode[]} | ||
*/ | ||
export default function getRootVNodes(node) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming-wise, I'd suggest something like getRootChildren
instead - getRootVNodes makes me think it'd be returning a virtual document fragment node, since that's the "root" as the DOM uses that term, but like you commented down on L28, we don't track vnodes for those document fragments 😞
return [...vNode.parent.children]; | ||
} | ||
|
||
vNode._rootNodes = getRootVNodes(vNode.parent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than needing to create new properties on every vnode, would it be lower overhead cache a map from shadowId to root children?
let result = null; | ||
|
||
for (const root of rootVNodes) { | ||
const foundNode = querySelectorAll(root, `#${token}`)[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For shadow root cases, this is going to essentially bypass QSA's selector-cache behavior that would otherwise make the ID lookup fast; we might want to consider making it smarter about that (findMatchingNodes
looks like it's meant to understand how to do it, but getNodesMatchingExpression
is going to early exit from the cached path anytime root
is not the document root)
No description provided.