Re: [editing] selection across editing host boundaries

On 23/06/2014 20:33 , Johannes Wilm wrote:
> I filed bugs on this on both Firefox and Chrome in spring 2013. It was
> briefly fixed in Chrome, but the fix was then retracted and we never
> heard any more of it. It was also reported in Firefox by someone else in
> 2011. [1]
>
> I also had some contact with Webkit people working in this area who
> unfortunately didn't find the behavior to be problematic.
>
> This is probably the main reason why at least I am wondering if editing
> maybe just is an area that won't be of enough interest to browser makers
> to ever fix and that it may therefore just make more sense to just ask
> them to remove it and do everything in Javascript. But now it seeems
> this is about to change, which would be really really good.

 From discussions I've had in the past on this topic, it's not so much 
that browser-folks don't want to fix this. The problem is more that 1) 
this is hard, so fixing can often only happen if someone really owns the 
problem rather than contributing the odd fix, and such a person isn't 
always available; 2) the current state of contentEditable is such a 
mess, notably with libraries doing per-UA workarounds, that fixes that 
make the behaviour globally saner actually break deployed code.

This has conspired to make the situation rot. Hopefully we can fix this 
with the new tack taken here, notably by using contentEditable=cursor as 
a "sanity hook" which doesn't have to be backwards compatible with the 
existing madness.

You point to several bugs, but I think the one most worth reading is:

     https://code.google.com/p/chromium/issues/detail?id=238000

This has a wealth of issues and information, as well as a number of good 
ideas. Piotr's point about the behaviour of deletion when a selection 
crosses an even number of editing boundaries is particularly useful.

The way we've been thinking about this so far is that with cE=cursor, 
the browser would perform no DOM modification on your behalf but rather 
you'd have to handle events in order to carry out the modifications 
yourself. This increases the amount of work that script has to do but in 
this case it would likely enable you to produce the precise handling you 
require.

Having said that, considering this specific problem makes me realise 
that the deletion event model that I had in mind does not work. The way 
I'd initially considered it was that you'd receive a Deletion event that 
would abstract away from the deleting backwards/forwards differences 
across platforms as well as reach of the deletion (character, word, 
line, etc.). This works well for the following examples (in all of which 
| represents a cursor and [] a selection):

<editable>If a clock gets hungry|, it goes back four seconds.</editable>

Here (at least on my computer) the following key combinations would 
yield deletion events with the indicated ranges:

backspace     "y"
delete        ","
alt-backspace "hungry"
ctrl-K        "If a clock gets hungry, it goes back four seconds."

So far so good, and this extends well to the selection case:

<editable>If a clock gets [hungry], it goes back four seconds.</editable>

backspace     "hungry"
delete        "hungry"
alt-backspace "hungry"
ctrl-K        "If a clock gets hungry, it goes back four seconds."

That's all nice and well, but what should the range be in the following 
cases:

   A:
   <editable>blah <non-editable>foo</non-editable>| blah</editable>

   B:
   <editable>blah [<non-editable>foo</non-editable>] blah</editable>

   C:
   <editable>bl[ah <non-editable>foo</non-editable> bl]ah</editable>

   D:
   <editable>blah <non-editable>f[oo</non-editable> bl]ah</editable>

   E:
   <editable>blah <non-editable>|foo</non-editable> blah</editable>

Or in this:

   F:
   <ul contenteditable=cursor>
     <li>blah</li>
     <li>|blah</li>
   </ul>

   F2:
   <ul contenteditable=cursor>
     <li>blah</li>
     <li>|</li>
   </ul>

or just for kicks, one could make the argument that this is different:

   G:
   <ul contenteditable=cursor>
     <li contenteditable=cursor>blah</li>
     <li contenteditable=cursor>|blah</li>
   </ul>

?

I think that we can start to solve this in the following manner:

   • When a deletion event would produce a range crossing an odd number 
of editing boundaries (either because there's a selection doing so in 
the sum of its ranges or for instance given backspace in case A above) 
then its range is empty and corresponds to a collapsed range a the 
cursor position (this is defined even for selections).

   • Conversely, when it would cross an even number of editing 
boundaries, then the range covers them.

   • Deletion events capture information expressing the direction of the 
deletion ("previous", "next", or possibly "both" in the delete-line 
case). This makes it possible for script to know how to hand the 
empty-range case.

   • Additionally, the deletion event can expose convenient information 
about the editing boundaries being crossed, their number, their hosts.

This means that, assuming backspace, the cases above can be handled as 
follows:

A: empty range, the script can decide whether to select or delete the 
non-editable content (direction "previous").
B: a range containing non-editable. Presumably deleted.
C: same as B with some extra content on both sides. Presumably deleted.
D: empty range, the script can decide what makes most sense. (Stabbing 
the user in the face sounds good.)
E: empty range, the script decides which is best.

For F, F2, G, and an awful lot of other cases (dt/dd, td, etc.) I think 
we should take the minimalist approach: just produce a deletion event 
indicating its direction but with an empty range. Scripts can decide if 
they wish to merge elements, delete empty ones, outdent, etc.


This is getting complicated enough (and I haven't mentioned other cases 
such as br, script, hr, td, img, video...) that I wonder if the Deletion 
event shouldn't have its own spec.

Other question: when there's a selection and an input event takes place, 
should it be preceded by a deletion event? I think we need to because 
the alternative is to have the input event handler have to perform its 
own logic equivalent to deletion, which would be painful. But it comes 
with its own interesting challenges.

Thoughts?


> Use cases for this:
>
> 1. We use it for footnotes which we float of to the right of the text in
> a <span class="footnote" contneteditable=false><span><span
> contenteditable="true">[FOOTNOTE TEXT]</span></span></span>. If one has
> access to CSS regions, one can even float them to be under the text on
> each page. The other <span class="footnote"> is used to draw the
> footnote number in the main text. If one hits backspace behind it, the
> entire footnote should disappear. as it is now, instead the "back wall"
> of the footnote is removed  which means that the rest of the paragraph
> is being added to the footnote.

A question for you: how would you like selection to work in this case, 
notably for copy/pasting? As a user, I would tend to expect that if I 
select from before the <sup>1</sup> to after it and copy, I would get a 
buffer containing the <sup>1</sup> but *not* the footnote content 
(otherwise you get the famed "PDF effect" with lots of junk in your 
buffer). It also looks visually weird if you have the footnote to the 
side of the page being selected. But with the logical document order you 
use, it would get selected. Do you make use of selection-preventing tricks?

These likely have their own amusing interactions with deletion: if you 
make the footnote non-selectable but wish to drop it when a selection 
encompassing it is deleted, you're facing a fun new challenge.

-- 
Robin Berjon - http://berjon.com/ - @robinberjon

Received on Tuesday, 24 June 2014 10:35:05 UTC