w3c / selection-api

Selection API
http://w3c.github.io/selection-api/
Other
46 stars 28 forks source link

`Selection.direction`'s behavior should be based on the node's position #334

Closed mozesstumpf closed 2 months ago

mozesstumpf commented 2 months ago

The main use cases where the direction can be useful:

  1. When we have an "in-range" selection (so it's not collapsed), and we press Shift + ArrowKey to navigate, it extends the focus boundary point.

  2. During the Selection, the engines handles the focus boundary point differently. For example, in Chromium, if the anchor is an editable, the focus boundary point has to be an editable as well, meanwhile Gecko allows the focus boundary-point to end in a non-editable node, therefore we may have to extend the focus to an editable node.

There may other use cases, but at the end, all that matters that we know which one is the anchor and the focus boundary-point.

With the current behavior of the direction that mainly relies on the user's interaction and changes the direction accordingly, is very confusing from the developer perspective, because the selection can be collapsed even if the direction is backward or forward, or it can be "in-range" even if the direction is none, so it's much harder to figure out which one is the anchor and focus boundary-point.

I'd like to discuss here that what's the purpose of the direction property with the current implementation, and what's the advantage of setting the direction based on the user's interaction instead of the node's position in the document.

mozesstumpf commented 2 months ago

I haven't clearly mentioned, but the problem is there with the current implementation, that lets say, the user presses the Shift + ArrowRight, so I have to extend the selection via script, so I set the focus with the Selection.extend.

From that point (since the direction is not node-position based), when I get the StaticRange from the getComposedRanges method, I can't know for sure that which one is the focus boundary-point, therefore I will not be able to determine that which node should I extend.

rniwa commented 2 months ago

I don't think we can change this behavior. Selection behavior is tied to the underlying platform's behavior / convention.

mozesstumpf commented 2 months ago

Selection behavior is tied to the underlying platform's behavior / convention.

I'm not sure whether you are talking about the direction or the selection's behavior. I'm completely fine with the selection's behavior, my concern is that I will not be able to determine the anchor and focus node within a ShadowRoot.

rniwa commented 2 months ago

Selection behavior is tied to the underlying platform's behavior / convention.

I'm not sure whether you are talking about the direction or the selection's behavior. I'm completely fine with the selection's behavior, my concern is that I will not be able to determine the anchor and focus node within a ShadowRoot.

What do you mean by that? Selection direction is neither forward nor backward when a user selects text with a mouse on macOS, and anchor & focus are not well defined in such cases.

mozesstumpf commented 2 months ago

What do you mean by that? Selection direction is neither forward nor backward when a user selects text with a mouse on macOS, and anchor & focus are not well defined in such cases.

Currently, we have access to the anchor & focus node via the Selection API (anchorNode, focusNode), but if the selection crosses the shadow boundary, these nodes will be rescoped to the shadow host, therefore we must rely on the direction property.

Let me demonstrate the issue in an example with a basic operation of the selection with keyboard (Shift + ArrowLeft/ArrowRight)

We have a text that will be selected with double click, which makes the direction's value to none.

// within a ShadowRoot
<div cE=true>Lorem |ipsum| dolor</div>

The Shift + ArrowLeft should extend the focus boundary-point, but since we can't determine that anymore due to the inconsistent behavior of the direction, we don't know that which boundary-point should be extended.

So, from that point, if I press Shift + ArrowLeft and update the bounday-point via script, that can be any of the following results:

<div cE=true>Lorem| ipsum| dolor</div>
// or
<div cE=true>Lorem |ipsu|m dolor</div>

After we changed the selection to any of the scenario above, we still can't rely on the direction to determine the focus boundary-point, since it may not be updated.

When the selection's range is mutated by scripts, e.g. via selectNode(node), direction of the selection must be preserved.

There is an example with comments about the basic implementation of the inline directional navigation with selection, that shows why I have to rely on the "nodeDirection".

https://codepen.io/IDontHaveAny/pen/RwzxMRB?editors=1010

rniwa commented 2 months ago

The Shift + ArrowLeft should extend the focus boundary-point, but since we can't determine that anymore due to the inconsistent behavior of the direction, we don't know that which boundary-point should be extended.

So, from that point, if I press Shift + ArrowLeft and update the bounday-point via script, that can be any of the following results:

<div cE=true>Lorem| ipsum| dolor</div>
// or
<div cE=true>Lorem |ipsu|m dolor</div>

The correct behavior on macOS is to always expand the selection. i.e. if Shift + ArrowLeft is pressed, then the selection should extend to the left, and if Shift + ArrowRight is pressed, then the selection should extend to the right.

mozesstumpf commented 2 months ago

The correct behavior on macOS is to always expand the selection. i.e. if Shift + ArrowLeft is pressed, then the selection should extend to the left, and if Shift + ArrowRight is pressed, then the selection should extend to the right.

But in both scenario, the focus boundary-point will be extended.

In the example above, I was talking about the manual implementation of these keys.

<div cE=true>Lorem |ipsum| dolor</div>
...................A......F
// A=anchor, F=focus

In this example, if the selection is in the Light DOM, I can determine the focus boundary-point with the Selection.focusNode/focusOffset. When the user presses the Shift + ArrowLeft, I can update the boundary-points via script for that:

<div cE=true>Lorem |ipsu|m dolor</div>
...................A.....F

If the selection is within a Shadow DOM, the Selection.focusNode/focusOffset will be rescoped to the shadow host, therefore I must use the getComposedRanges that returns a StaticRange. The problem is that I can't know for sure that the startContainer/startOffset or the endContainer/endOffset is the focus boundary-point.

rniwa commented 2 months ago

But in both scenario, the focus boundary-point will be extended.

That's not true. The correct behavior is to pick the left edge if left arrow is pressed and pick the right edge if the right arrow is used since there is no directionality defined in such cases.

mozesstumpf commented 2 months ago

The correct behavior is to pick the left edge if left arrow is pressed and pick the right edge if the right arrow is used since there is no directionality defined in such cases.

If the Shift key is not pressed, then it collapses to the edge, but if the Shift is pressed, then it extends the focus boundary-point.

The video was recorded in windows, but I've checked it on macOS as well and that has the same behavior.

https://github.com/user-attachments/assets/84cd54d5-732f-408b-a05a-4b79a8bc8f71

rniwa commented 2 months ago

The correct behavior is to pick the left edge if left arrow is pressed and pick the right edge if the right arrow is used since there is no directionality defined in such cases.

If the Shift key is not pressed, then it collapses to the edge, but if the Shift is pressed, then it extends the focus boundary-point.

You need to double click on a word to select the word on macOS instead of drag-selecting it.

mozesstumpf commented 2 months ago

Oh, now I get that why it has to be directionless, thanks!

There is one more thing might worth to mention, in the User Interactions:

set update selection's direction to forwards if the start is before or equal to the end, backwards if if the end is before the start

In this statement, we should refer to anchor and the focus instead of the range's boundary points, aren't we?