w3c / selection-api

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

Improve selection.isCollapsed #337

Open abhish-shrivastava opened 1 month ago

abhish-shrivastava commented 1 month ago

The selection.isCollapsed works by detecting start and end nodes and their offsets. This sometimes leads to inconsistencies because of different implementations by different browsers (especially on mobiles, tested this on chrome). Here is what happens-

  1. When a cursor is placed at the beginning of a list, the selection.isCollapsed should ideally return true and selection.type as 'cursor'.
  2. However, if there is text (say a paragraph block previously to the list), then it still gives false because startContainer is then taken as the previous text block (while end container points correctly to the list item, li of the list). So even though nothing is selected and it is just a cursor, selection.isCollapsed returns false and selection.type as 'range'.
rniwa commented 1 month ago

Could you provide a sample HTML? It's hard to discuss this without a concrete DOM tree.

abhish-shrivastava commented 1 month ago

Sure, here is a simple example-

<p>....some text...</p>
<ul>
<li>list 1....</li>
<li>list 2...</li>
.
.
.
</ul>

When a cursor is placed at the start of 1st li (list 1), ideally the startContainer should point to that li with offset 0, Same for endContainer. And that is how it is on desktop browsers. But on mobile (checked this on android/chrome), endContainer points correctly but startContainer points to the text from the previous paragraph block and takes offset from there.

Just like that if I place cursor at the beginning of the second li (list 2), startContainer this time points to the 1st list item and takes offset from there.

Thus wrongly, selection.isCollapsed returns false and selection.type as 'range'. Also selection's range object's range.collapsed also returns wrongly as false. This is due to faulty implementation by browsers.

Here can be an alternate solution to address this. To find isCollapsed property, instead of checking startContainer and endContainer and their offsets-

  1. We can check selection's range object's range.getBoundingClientRect().width. If it is 0 then it is a cursor else it is a range.

or

  1. To be more comprehensive, we can get selected contents using selection's range objects range.cloneContents() and check if it is empty (not containing any text or image etc.).
rniwa commented 1 month ago

That sounds like a bug on Chrome / Android.

abhish-shrivastava commented 1 month ago

I respectfully disagree. While a case can be stated that mobile browsers need to work on implementing correct startContainer but still, if nothing is selected and what is blinking on my screen in an editor is a cursor, then selection.type should return 'cursor' and selection.isCollapsed as true, irrespective of from where start and end container references and their offsets are taken.