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

document.caretPositionFromPoint API in shadow DOM scenario #949

Closed
1 task done
siliu1 opened this issue Apr 25, 2024 · 6 comments
Closed
1 task done

document.caretPositionFromPoint API in shadow DOM scenario #949

siliu1 opened this issue Apr 25, 2024 · 6 comments
Assignees
Labels

Comments

@siliu1
Copy link

siliu1 commented Apr 25, 2024

こんにちは TAG-さん!

I'm requesting a TAG review of document.caretPositionFromPoint API in shadow DOM scenario.

The document.caretPositionFromPoint returns a CaretPosition which represents the caret position indicating current text insertion point. However, the current spec is vague about the behavior in shadow DOM scenario. The proposal attempts to specify the behavior in shadow DOM.

Further details:

  • I have reviewed the TAG's Web Platform Design Principles
  • Relevant time constraints or deadlines: None
  • The group where the work on this specification is currently being done: CSSWG
  • The group where standardization of this work is intended to be done (if current group is a community group or other incubation venue): CSSWG
  • Major unresolved issues with or opposition to this specification: We have not heard of opposition, the proposal has been discussed in CSSWG and came to an agreement.
  • This work is being funded by: Microsoft

[1] document.caretPositionFromPoint in shadow DOM scenario explainer:

Use case/developer need

It's necessary for web developers to have a method to determine the caret position within shadow roots. This allows for indentifying node
and offset within the shadow root so that range/selection creation is possible in shadow root at specific point.

With the proposal, web developer can get caret position inside shadow root like:

var caret_position = document.caretPositionFromPoint(x, y, shadowRoot1);
// caret_position.offsetNode is inside shadowRoot1
// caret_position.offset is the offset within offsetNode
// caret_position.getClientRect() returns a DOMRect which would be the caret bounds in shadowRoot1.

Background

The document.caretPositionFromPoint API returns a
CaretPosition which represents the caret position indicating current
text insertion point. However, the current spec doesn't
specifically define if and how shadow DOM scenarios should be supported. We want to update the API and spec to support shadow DOM cases
in a well defined way.

Proposal

Similar to Selection.getComposedRanges API, We should
introduce another argument shadowRoots which is the list of shadow roots that the API can pierce into. The API will look like:

document.caretPositionFromPoint(double x, double y, ShadowRoot... shadowRoots)

the returned caret position will only be in the shadow root if it's provided in shadowRoots argument. Otherwise, the caret position will
be in the shadow host's parent at the offset of the shadow host.

The proposed change to the API maintains the boundary of closed shadow tree unless the shadow root is provided to the API.

Alternative considered

  • Do not let this API pierce into shadow tree. Instead, put this API on DocumentOrShadowRoot.

The con to this approach is that the usage might be cumbersome to get a position inside nested shadow roots (need to call the API on each
level of shadow root).


@martinthomson
Copy link
Contributor

@siliu1 have you reviewed the design principles?

@zcorpan
Copy link

zcorpan commented May 7, 2024

* Primary contacts (and their relationship to the specification):
  
  * Siye Liu (@siliu1, Microsoft, implementor)
  * Simon Pieters (@zcorpan, Mozilla, spec author)

I reviewed the spec change, but did not write it.

@siliu1
Copy link
Author

siliu1 commented May 7, 2024

Updated description based on above feedbacks.

@LeaVerou
Copy link
Member

Hi there,

We looked at this during a breakout today.

First, the purpose of the explainer is not just to assist in TAG review, but to provide an overview of what the spec does, why it’s needed, and why it’s designed that way, for everyone. Including an explainer in the TAG review issue does not help with that.

Second, one of the most important sections of the explainer is user needs. Here there are none. Getting the caret position is not a use case, it’s a solution. Why do they need to get the caret position?

This is essential for evaluating the design. For example, if addressing the common use cases requires getting all shadow roots in a document and passing them to this function, the ergonomics are suboptimal, since the developer would have to query all elements, traverse them to see which ones have an open shadow root, then pass them to the function:

let shadowRoots = [];
for (let el of document.querySelectorAll("*")) {
	if (el.shadowRoot) shadowRoots.push(el.shadowRoot);
}
let pos = document.caretFromPoint(x, y, shadowRoots);

But if it’s not common to need to do it in such a sweeping manner, but more targeted to a few shadow roots, it may be acceptable to provide them explicitly. Without a good set of use cases, we simply cannot know.

The shadowRoots parameter does double duty here: it opts in to the new behavior AND provides the shadow roots the code knows about. Again, depending on the use cases, this could be ok, but if aggregation is often desired (fetching all open shadow roots under a container), it can get tedious. It may be worth exploring alternative designs where this is easily possible.

Do note that we advise against optional parameters being positional, and recommend a dictionary instead which both makes function calls self-explanatory and affords room for future expansion.

As an example, one possible design could be to make the parameter a dictionary with a shadowRoots option, which could take either an array of Node instances, or a Node (which would be treated the same as an array of one element). For non-ShadowRoot nodes, all open shadow roots within that container would be fetched. This means that shadowRoots: document addresses the use case of getting all open shadow roots with no further complication to the API, and closed shadow roots can still be provided like shadowRoots: [document, shadow1, shadow2, ...].

We also noticed that elementFromPoint() and elementsFromPoint() still only have x and y parameters. Modifying the signature of one but not the other breaks consistency and should be avoided.

@siliu1
Copy link
Author

siliu1 commented Jun 19, 2024

Hi Lea,

Thank you for the review.

The latest spec of this API has been changed to take a dictionary with shadowRoots option: https://drafts.csswg.org/cssom-view/#ref-for-dom-document-caretpositionfrompoint.

Regarding the open shadow roots vs. closed shadow roots, there is an existing API Element.getHTML that takes a dictionary with a shadowRoots option for both open and closed shadow roots. I think it's better to make it consistent across APIs.

@LeaVerou
Copy link
Member

Thank you for being responsive to our feedback.

Indeed, consistency is a good point, though depending on the use cases, it could end up being a good choice to change both of these methods. Therefore, it would still be good to list use cases (starting with user needs).

Nevertheless, we see a shadowRoots argument as a low-level primitive that could later be expanded so it's ok to start from it, especially if it has already shipped in other methods. I would reiterate that elementFromPoint() could also be expanded in the same way for consistency (and I suspect there are plenty of use cases for that too).

It's great to see support from additional implementers.

We’re going to go ahead and close this. Thank you for flying TAG!

@torgo torgo removed this from the 2024-06-24-week:a milestone Jun 24, 2024
@LeaVerou LeaVerou added the Resolution: satisfied The TAG is satisfied with this design label Jun 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants