Open dizhang168 opened 1 month ago
Personally, I think the getComposedRanges() API would be much more valuable if it was traversing using a flat tree. I also do agree this is a significant change. I wonder how hard it would be to change to flat tree if we ship getComposedRanges first using only composed tree?
This is not just getComposedRanges
. It will affect the semantics and rendering of selection. I don't think we can change the definition of selection once any API has shipped.
Another option is to make selection to support multiple ranges, but I guess this is more complicated than using flat tree....
@rniwa Ryosuke, do you mind clarify what you meant by the definition of selection? Is it selection should be in composed tree..or what else ?
I did an audit of the existing Selection API that uses DOM tree traversal and where a Flat tree traversal would be more useful and visual:
Specifically, for Selection::setBaseAndExtent, I wrote out a proposal of how we can change/add to the existing API to allow a flat tree position comparison to set the base and extent: https://github.com/dizhang168/shadow-dom-selection/blob/main/flat-tree-setBaseAndExtent.md We should reach out to Editor authors to better understand their pain points with slots. If we want to fix all the existing Selection/Range APIs above to use flat tree, then that is a whole new project.
The more immediate question is scoping how getComposedRanges() behave. Blink prefers to use the flattened tree to set the composed range endpoints it exposes. These endpoints will only be accessed by this API and wouldn't change any existing behavior. This way, the output of getComposedRanges() would match the computed visible selection.
@johanneswilm could you Agenda+ this for the upcoming Editing meeting? Thank you!
After talking with the Blink team, we have come to an agreement that changing the Selection API to use flat tree traversal would be a huge project. The proposal of supporting multiple ranges for Selection is something we cannot implement currently. Adding more APIs should also be considered after a thorough new compatibility investigation. Surveying the existing feature requests, most complaints are about selection across trees not returning the same between browsers and less about specific slot edge cases. Since our goal is to unblock the web developers who depend on the Selection API, we agree to move forward with an implementation of getComposedRanges() using Composed DOM tree traversal.
With all the good conversations so far, it is obvious a flat tree traversal can help solve the discrepancy across browsers when it comes to how elements are selected. Some use cases include selection by dragging and converting a selection to string. We should keep this issue open and continue the discussion.
(I'd like to document what I have in mind to support flat tree)
We need to introduce flat-before
, flat-equal
and flat-after
algorithms to allow us comparing boundaries in flat tree
~and these algorithms will be used to the following API~ First, we keep these APIs as is
Selection::setBaseAndExtent
Range::setStart/setStartBefore/setStartAfter
Range::setEnd/setEndAfter/setEndBefore
Instead, we use Di's proposal to introduce a new set of setter APIs to allow web authors to use the flat tree algorithms. So that the true start
and true end
that match the visual selection will be stored.
And we also shouldn't change the following APIs because they should remain as is to not break the encapsulation of shadow trees.
Range::isPointInRange
Range::comparePoint/compareBoundaryPoints
Range::intersectsNode
Range::commonAncestorContainer
So we probably need a new set of APIs that can expose information across the boundary. Maybe we should introduce a new range call CrossShadowBoundaryRange
that can only be returned from getComposedRanges
. This range supports APIs to allow web authors to get results that match the what users see on screen.
This surfaced from the conversation at TPAC 2024 about whether we should use a Flat Tree Traversal (Meeting notes) for comparing Selection positions that cross shadow trees.
Composed Tree
With the Composed Tree, we do the normal DOM tree traversal, but consider the owner document to be the common ancestor. Why this is good:
Flat Tree
With the Flat Tree, we traverse a flattened tree where the shadow trees are flattened to all be contained within one tree. Why this is good:
Open questions
Examples:
Selection where start and end are in the same tree, but slotted to be in reverse order. Selection start from the sibling of shadow host, end inside the slotted content. https://github.com/mfreed7/shadow-dom-selection?tab=readme-ov-file#3-slotted-content
Some use cases
Bidi, tables, flex/grid layout maybe.
Related issues
Need spec changes to Range and StaticRange to support nodes in different tree? #169 Serialization of the current selection, when that selection crosses shadow roots #163