2

I'm working on a simply syntax highlighter that replaces text with dom elements with classes.

Say, I have a

<div contenteditable="true">
  Some text. | And some other text.
</div>

and the cursor is at the | pipe

//if a user types foo

<div contenteditable="true">
  Some text. foo| And some other text.
</div>

// and I replace it, then set the selection after the inserted element

<div contenteditable="true">
  Some text. <span class="highlight-foo">foo</span>| And some other text.
</div>

but if you type, you type into the span..no matter what.

//type bar

<div contenteditable="true">
  Some text. <span class="highlight-foo">foobar|</span> And some other text.
</div>

and I don't want that, but I can't set the selection right after the newly inserted element.

<div contenteditable="true">
  Some text. <span class="highlight-foo">foo</span>bar| And some other text.
</div>

here's the js that does the highlighting and replacing..

...
// chunk is the variable that holds foo, if we stick the the above example..
// select it
range.setStart(range.startContainer, range.startOffset-chunk.length);

// add it to the range
sel.addRange(range);

// replace it..then set the range to the textNode after the span
// because after the injection selection.anchorNode is the textNode inside the span..
// in chrome, however, this below sets the range correctly.
range.setStart(sel.anchorNode.parentNode.nextSibling, 0);

// and it's all good until now, but adding the range to the selection does nothing..
sel.removeAllRanges();
sel.addRange(range)

// the selection is still inside the span..

How to solve that? oO I've read a lot on it even looked a fair amount of questions on here, but nothing regarding this particular problem.

tenshou
  • 477
  • 4
  • 12

1 Answers1

6

I assume you're testing this in WebKit. WebKit's caret handling prevents you from placing the caret at the start of a text node: https://bugs.webkit.org/show_bug.cgi?id=23189. In Firefox your code should be fine.

The workarounds are all horrible. One is to insert a zero-width space (such as \u200B) and select it, but then you have add special code to handle arrow keys and code to remove the zero-width spaces.

Update

Another workaround is to use the fact that WebKit has a special case for links where it forces the caret to go after the link:

http://jsfiddle.net/RfMju/

So what you could do, which is unpleasant but doable, is to use a <span> when you want the caret to go inside the highlighted element and change it into a link otherwise.

Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • I see, yes, that must be it, because it does seem to work on gecko.. thank you, I accept your answer. I'm a bit reluctant to use such workarounds, though it seems there's no other way for now. thank you – tenshou May 11 '12 at 08:51
  • @tenshou: I found another workaround that may be better. I updated my answer. – Tim Down May 11 '12 at 08:53
  • that is nice, thank you. it does eliminates the extra work of deleting zero-with spaces. and it seem to work absolutely, you can't enter the link even with clicking or with the arrow keys, whereas there's a slim boundary for gecko and if you aim right you can place the cursor right into the link, or just right after it, but seeing the characters it would still be on the same offset. but that's another story. thank you! – tenshou May 11 '12 at 09:57
  • Does this still work? I am running the fiddle but I can enter the link text and edit it. – Jonathan Andersson Mar 03 '17 at 10:20