Bug 15256 - Impossible to place an editable selection inside empty elements
: Impossible to place an editable selection inside empty elements
Status: NEW
: WebKit
HTML Editing
: 523.x (Safari 3)
: All All
: P2 Normal
Assigned To:
: http://www.fredck.com/bugs/safari/col...
: GoogleBug, InRadar, ReviewedForRadar
:
: 9915 23600
  Show dependency treegraph
 
Reported: 2007-09-22 02:58 PST by
Modified: 2014-02-05 01:42 PST (History)


Attachments
TC (2.14 KB, text/html)
2007-09-22 03:19 PST, Frederico Caldeira Knabben
no flags Details
automated test case (incomplete) (2.38 KB, text/html)
2007-11-26 06:46 PST, Alexey Proskuryakov
no flags Details


Note

You need to log in before you can comment on or make changes to this bug.


Description From 2007-09-22 02:58:50 PST
There is now way to place the caret inside an empty element for editing. For example, suppose we have the following HTML:

    Some <b></b>text.


When using the DOM range and selection to move the caret inside the <b></b>, the editing point will be after it instead:

    Some<b></b>|text.

The expected result:

    Some<b>|</b>text.

The specified URL is a simple test case for it, which makes it easy to understand the problem. It works well with Firefox 2 and Opera 9.5 (minor issues with this one).
------- Comment #1 From 2007-09-22 03:19:03 PST -------
Created an attachment (id=16346) [details]
TC
------- Comment #2 From 2007-09-22 05:04:38 PST -------
Sorry... small correction... for correctness.

Today, the caret is actually placed "before" the <b></b> in the above example, not after it. So we are having the following result:

    Some |<b></b>text.
------- Comment #3 From 2007-11-22 09:22:22 PST -------
<rdar://problem/5610785>
------- Comment #4 From 2007-11-22 10:55:03 PST -------
This problem doesn't only occur for empty text nodes - even if the node is not empty, selection position is adjusted to start before its container.

I think I have a workaround that might work. It involves zero-width space:
        [<b>&#x200b;</b>]
...
    range.setStart( el.firstChild, 1 ) ;
    range.setEnd( el.firstChild, 1 ) ;

This positions the insertion point after the invisible space, and inside the text child node.

Obviously, the space needs to be removed when the user starts typing, or clicks somewhere else - otherwise, arrow navigation will not work incorrectly, stopping at this position twice.

Please let us know if this works for you.
------- Comment #5 From 2007-11-22 11:48:58 PST -------
(In reply to comment #4)
> Obviously, the space needs to be removed when the user starts typing, or clicks
> somewhere else - otherwise, arrow navigation will not work incorrectly,
> stopping at this position twice.

I've tried something similar before, but the above thing is the big problem. It is quite difficult to provide a stable cleanup mechanism in JavaScript, to remove the  zero-width space text node. There are too many combinations to consider, like keyboard navigation, mouse click, API calls, clipboard pasting, etc... it would be easy to have zero-width space chars laying around, causing an undesired behavior. At the end, the cleanup code would be so huge that it would be overkill for a JS app. This is why we need the browser to handle it properly.
------- Comment #6 From 2007-11-26 06:35:33 PST -------
I tried to quickly fix this today, but the problem is much deeper than just the addRange() function - it's the underlying selection object that enforces "valid" positions. It's clear to me that it should be redesigned, but that may take a while.
------- Comment #7 From 2007-11-26 06:46:29 PST -------
Created an attachment (id=17529) [details]
automated test case (incomplete)

This tests that ranges round-trip properly, but it is not enough to fix this bug - editing commands should avoid validating selections in some situations, too.
------- Comment #8 From 2007-11-26 16:42:22 PST -------
Is this a regression from Safari 2.0.4 or any released (non-beta) version since then?
------- Comment #9 From 2007-11-26 21:33:15 PST -------
This is not a regression - but it looks like a major limitation without a viable workaround.
------- Comment #10 From 2007-11-28 04:18:46 PST -------
Marking this bug as critical for FCKeditor.
------- Comment #11 From 2007-12-29 11:17:56 PST -------
See also: bug 6350.
------- Comment #12 From 2009-02-03 01:54:57 PST -------
See also: bug 21785, bug 20242, bug 20129.
------- Comment #13 From 2009-03-17 05:02:49 PST -------
See also bug #23189
------- Comment #14 From 2009-10-24 04:30:59 PST -------
What is happening with this bug?
------- Comment #15 From 2010-02-17 04:28:44 PST -------
Is there a reason this bug is now 2+ years old without action?  Please target this for patching.
------- Comment #16 From 2010-03-06 18:27:51 PST -------
I would also like to request a redesign of the selection/caret behaviour, or e.g. to extend the default behaviour with CSS hints to allow you to control possible caret placements yourself.

There are many cases where you would want to use inline elements, but still allow the caret to distinguish between being inside and outside the element in question.
------- Comment #17 From 2010-09-02 10:13:43 PST -------
Also calling for a fix. I've hit this bug many times, and have tried all sorts of hacks to get around it (e.g. appending text nodes on keypress, zero-width spaces etc).
------- Comment #18 From 2010-09-03 04:11:31 PST -------
Just FYI... three years passed, and this issue is still highly critical for CKEditor.

Unfortunately we're not able to code a fix for it. Is there anything else we could do to have someone involved on fixing it?
------- Comment #19 From 2010-09-03 10:02:53 PST -------
We're opening a bounty for a definitive fix to this issue:
https://lists.webkit.org/pipermail/webkit-dev/2010-September/014251.html

I really hope we'll have success with it.
------- Comment #20 From 2010-09-06 02:44:33 PST -------
(In reply to comment #19)
> We're opening a bounty for a definitive fix to this issue:
> https://lists.webkit.org/pipermail/webkit-dev/2010-September/014251.html
> 
> I really hope we'll have success with it.

The problem with this bug is that resolving this bug requires our rewriting much of the existing editing code.  And that isn't easy by no means.

Could you explain how this bug is impacting CKEditor?  It seems like you're trying to apply styles to a selection?  I'd like to know more context of the problem you're encountering so that we might be able to come up with an alternative solution to your problem.
------- Comment #21 From 2010-09-07 02:54:56 PST -------
There is now a bounty on this bug:

<https://lists.webkit.org/pipermail/webkit-dev/2010-September/014251.html>
------- Comment #22 From 2010-09-07 07:46:10 PST -------
(In reply to comment #20)
> Could you explain how this bug is impacting CKEditor?  It seems like you're trying to apply styles to a selection?

That's exactly the problem. The detail is that we're talking about a "collapsed" selection. I mean, just have the caret blinking inside the editor, hit CTRL+B for Bold (bug: nothing is happening at this point) and type some text. There will be no style applied.

Just to give you more details, here you have the code being executed:

1st part, this small if block. Note that even if we're using an internal range manipulation class, it works much like the DOM range implementation of WebKit:
http://dev.ckeditor.com/browser/CKEditor/trunk/_source/plugins/styles/plugin.js?rev=5847#L360

2nd part - it's a bit more complicated, but basically we're transforming our internal range representation in a native WebKit range and selecting it:
http://dev.ckeditor.com/browser/CKEditor/trunk/_source/plugins/selection/plugin.js?rev=5812#L1040

The attached TC reflects the above exactly.
------- Comment #23 From 2010-09-07 09:49:27 PST -------
(In reply to comment #22)
> (In reply to comment #20)
> > Could you explain how this bug is impacting CKEditor?  It seems like you're trying to apply styles to a selection?
> 
> That's exactly the problem. The detail is that we're talking about a "collapsed" selection. I mean, just have the caret blinking inside the editor, hit CTRL+B for Bold (bug: nothing is happening at this point) and type some text. There will be no style applied.

Could you call document.execCommand('bold', false, null) instead of manually adding bold?  We have typing style internally, and bold should be preserved when user starts typing.  I bet other browsers would support this as well.

We also have applyStyle command internally, which allows you to apply arbitrary CSS style on the selected region.  It's used by bold, italic, etc... as well as for style preservation.  We could expose either if that'll benefit you and other web developers.
------- Comment #24 From 2010-09-07 09:50:40 PST -------
By either I meant typing style and applyStyle command.
------- Comment #25 From 2010-09-08 08:02:10 PST -------
(In reply to comment #23)
> Could you call document.execCommand('bold', false, null)

Well, that's the way we have it in FCKeditor 1.0... we were just using the standard execCommand stuff. But, later we've found limitations in this approach. Let me give you more info.

The very first issue is being able to produce the same output with all browsers. For the bold command, you have some browsers producing <b>, while others use <strong>. Depending on the setting, you can even have <span style="font-weight: bold;">, or the even worst <span class="Apple-style-span" style="font-weight: bold;">.

Consider that we're talking just about the bold command, but almost all commands have different implementation among browsers.

The other important factor is that CKEditor is extremely flexible, when considering styles. One can decide how to exactly apply styles. For bold, for example, you can easily choose to output <b>, <strong>, <span style="font-weight: bold;">, <span class="myBold">, or any other tag you want.

So far, we were just talking about bold... but you must also consider custom styles, which doesn't have their execCommand equivalent.

It has been always my opinion: execCommand is one of the worst things one could have for styling. It's badly designed and limited. It's a pity to see it's making its space in the HTML5 standards. The future doesn't look better because of it.

---

> We also have applyStyle command internally, which allows you to apply arbitrary CSS style on the selected region.  It's used by bold, italic, etc... as well as for style preservation.  We could expose either if that'll benefit you and other web developers.

Is this command that flexible, so you can apply a style that outputs not only <span style="..."> tags, but also things like <span class="xyz">, <b dir="rtl">, <font face="ABC">, etc.? In that case, it would be just perfect to workaround the problem, even if still not a real solution for the root of the problem.
------- Comment #26 From 2010-12-06 09:01:56 PST -------
I found a workaround, don't know if anybody found this before but instead of programmatically appending a zero-width space, you can do the same thing with the css3 content property in the after psuedo-selector of the elements you want to put the caret in. This has the advantage that the extra characters don't show up in the DOM and the user can't navigate the caret between it. So basically it doesn't need cleaning up.

For this to work for any child element of your content editable element it would be something like this:

    #mycontenteditableelement *:after {
        content: '\200B';
    }

I didn't check completely, but I suspect this is a full workaround.
------- Comment #27 From 2010-12-06 09:35:00 PST -------
(In reply to comment #26)
>     #mycontenteditableelement *:after {
>         content: '\200B';
>     }
> 
> I didn't check completely, but I suspect this is a full workaround.

I'll just comment that the support for after, before, first-letter, and other pseudo-selectors are poorly supported in the content editable area.  In the worst case, WebKit crashes or reaches an inconsistent state.  So I wouldn't recommend this.
------- Comment #28 From 2010-12-06 10:02:38 PST -------
(In reply to comment #27)
> I'll just comment that the support for after, before, first-letter, and other pseudo-selectors are poorly supported in the content editable area.  In the worst case, WebKit crashes or reaches an inconsistent state.  So I wouldn't recommend this.

Good to know. Do you or anybody else have any advice as to what to avoid specifically? In my limited use case (adding new <p>'s in response to keypress events and moving the caret inside the newly created <p>) I am seeing no unwanted side effects whatsoever with Safari 5.0.3.
------- Comment #29 From 2010-12-07 13:32:59 PST -------
While the (In reply to comment #28)
> Good to know. Do you or anybody else have any advice as to what to avoid specifically? In my limited use case (adding new <p>'s in response to keypress events and moving the caret inside the newly created <p>) I am seeing no unwanted side effects whatsoever with Safari 5.0.3.

Thanks for sharing your findings.

While the provided hack didn't fix the issue in CKEditor (after a quick test, still to be investigated), I've noticed that it has broken the arrow navigation inside the editor. Basically, the RIGHT-ARROW key remained stuck when reaching the right boundary of any inline element (like a <strong>).
------- Comment #30 From 2011-06-20 17:20:58 PST -------
Any news on this. I keep running into this bug and variations of it. For example, it's impossible to put the caret at the beginning of ANY text node. This is quite a silly bug to have in WebKit for so long.
------- Comment #31 From 2011-06-20 17:22:47 PST -------
(In reply to comment #30)
> Any news on this. I keep running into this bug and variations of it. For example, it's impossible to put the caret at the beginning of ANY text node. This is quite a silly bug to have in WebKit for so long.

There's an active discussion on whatwg about VisiblePosition's canonicalization.  Please respond on the thread if you're interested in this being fixed in the long term.
------- Comment #32 From 2011-06-20 17:27:30 PST -------
@Ryosuke Niwa - Do you have a link?
------- Comment #33 From 2011-06-20 17:32:20 PST -------
(In reply to comment #32)
> @Ryosuke Niwa - Do you have a link?

http://www.mail-archive.com/whatwg@lists.whatwg.org/msg27063.html
------- Comment #34 From 2011-08-22 11:09:30 PST -------
See also: bug 66630.
------- Comment #35 From 2012-02-01 10:22:36 PST -------
Any update on this?
------- Comment #36 From 2012-05-01 21:24:41 PST -------
*** Bug 10884 has been marked as a duplicate of this bug. ***
------- Comment #37 From 2012-08-11 19:00:25 PST -------
It is also, as far as I can tell, impossible to place the insertion point *between* elements.  This makes a number of interesting programmatic uses of insertHTML impossible (unwrapping nodes, for example).

For example, given an HTML block like this:


    <div contentEditable="true"><div id="scratchpad"></div></div>


and code like this:


    document.getElementById("scratchpad").innerHTML = "<div id=\"foo\">blah</div><div id=\"bar\">blah</div>";

    var sel = window.getSelection();
    sel.removeAllRanges();
    var range = document.createRange();

    range.setStartAfter(document.getElementById("foo"));
    range.setEndAfter(document.getElementById("foo"));
    sel.addRange(range);

    document.execCommand("insertHTML", false, "<div id=\"baz\">-</div>");



One would expect this snippet to result in:

    <div id="foo">blah</div><div id="baz">-</div><div id="bar">blah</div>

but instead, you get:

    <div id="foo">blah</div><div id="bar">-blah</div>

I've tried every combination of set{Start|End}{After|Before|} that I can think of, and even things like setBaseAndExtent, modifying the selection object directly by extending it in either direction, etc.  Nothing works.
------- Comment #38 From 2014-02-05 01:42:28 PST -------
Any progress on this bug? It is still reproducible in the JavaFX implementation of WebKit.