Skip to content
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

Reference Target: Clarify how JS Element attributes work with referenceTarget #1066

Merged
merged 1 commit into from
Aug 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions proposals/reference-target-explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This proposal is based on [@Westbrook](https://github.com/Westbrook)'s [Cross-ro

### Cross-Root ARIA

For an in-depth description the cross-root ARIA problem, see [@alice](https://github.com/alice)'s article [How Shadow DOM and accessibility are in conflict](https://alice.pages.igalia.com/blog/how-shadow-dom-and-accessibility-are-in-conflict/). The article describes the two main problems that need to be solved:
For an in-depth description the cross-root ARIA problem, see [@alice](https://github.com/alice)'s article [How Shadow DOM and accessibility are in conflict](https://alice.pages.igalia.com/blog/how-shadow-dom-and-accessibility-are-in-conflict/). The article describes the two main problems that need to be solved:

#### 1. Referring from Shadow DOM outwards

Expand Down Expand Up @@ -295,14 +295,27 @@ Reference target does not change the behavior of the host element when it is nes

Some JavaScript attributes reflect HTML attributes as Element objects rather than ID strings. These include:

- ARIAMixin attributes like `ariaActiveDescendantElement`
- `ARIAMixin.ariaActiveDescendantElement`
- `ARIAMixin.ariaControlsElements`
- `ARIAMixin.ariaDescribedByElements`
- `ARIAMixin.ariaDetailsElements`
- `ARIAMixin.ariaErrorMessageElements`
- `ARIAMixin.ariaFlowToElements`
- `ARIAMixin.ariaLabelledByElements`
- `ARIAMixin.ariaOwnsElements`
- `HTMLButtonElement.interestTargetElement`
- `HTMLButtonElement.popoverTargetElement`
- `HTMLElement.anchorElement`
- `HTMLInputElement.form`
- `HTMLInputElement.labels`
- `HTMLInputElement.list`
- `HTMLLabelElement.control`
- _(This list is not exhaustive)_

These will _always_ refer to the **host** element that they're targeting, and _never_ the referenceTarget element directly. This behavior maintains the design that an [IDL attribute with type Element](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:element) can only refer to an element that is a descendant of a [shadow-including ancestor](https://dom.spec.whatwg.org/#concept-shadow-including-ancestor) of the element hosting the attribute.
These will _never_ directly return the referenceTarget element that's inside the shadow tree. This is because an [IDL attribute with type Element](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:element) can only refer to an element that is a descendant of a [shadow-including ancestor](https://dom.spec.whatwg.org/#concept-shadow-including-ancestor) of the element hosting the attribute.

Instead, most attributes return the **host** element that they're targeting, as long as the attribute's expected type is `HTMLElement`. However, The `.form` and `.list` attributes will return `null` when used with a referenceTarget, because they are expected to be `HTMLFormElement` or `HTMLDataListElement` and the host element itself is not a form or datalist. Importantly, the underlying association will still exist: the input will be connected to the form, for example; it's just not reflected by the `.form` attribute.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a little odd, doesn't it? There is a form/list reference, and it's valid, but the JS accessor returns null. Perhaps it'd be better to have the IDL for form and list return HTMLElement (or even Element?)?

I suppose there are likely web compat issues with this, of some variety.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree it seems odd. My main concern was worrying not to break the web by changing the return type to HTMLElement instead of HTMLFormElement/HTMLDataListElement. That said, the change would only affect new scenarios that were previously not possible, and it should not affect existing code as far as I can tell. It would be good to have an expert weigh in on the potential problems with changing the IDL return types in the new scenarios.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to hold this PR for this feedback or open a new PR once we've heard from other implementors about possible issues here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would benefit from further discussion, but I don't think it should block this PR for now. I logged #1072 to discuss the follow-up options. Depending on the outcome of the discussion, another PR could be made to update this section.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Thanks Ben!


> Note: It may be possible to add new attributes `.formElement` and `.listElement`, which could return the host element. However, that is beyond the scope of this proposal.

In the example below, `input.ariaControlsElements` is the `<fancy-listbox>` element that was targeted by `aria-activedescendant="fancy-listbox"`, even though the active descendant internally targets `<div id="option-2">`.

Expand All @@ -323,7 +336,27 @@ In the example below, `input.ariaControlsElements` is the `<fancy-listbox>` elem
<script>
const input = document.getElementById("input");
console.log(input.ariaControlsElements);
// [<fancy-listbox id="fancy-listbox">]
// Logs: [<fancy-listbox id="fancy-listbox">]
</script>
```

This example shows a submit button connected to a form inside a shadow tree. The button's `.form` attribute returns `null`, but the button _is_ still associated with the form, and clicking it will submit the form.

```html
<button id="submit" type="submit" form="fancy-form">Submit</button>
<fancy-form id="fancy-form">
<template
shadowrootmode="open"
shadowrootreferencetarget="real-form"
>
<form id="real-form"></form>
</template>
</fancy-form>

<script>
const submit = document.getElementById("submit");
console.log(submit.form); // Logs: null
submit.click(); // Submits <form id="real-form">
</script>
```

Expand Down