I am trying to create a contenteditable div, in which the user, when editing, does not edit the text within certain HTML tags (or conversely, can ONLY edit the the text within certain tags: either achieves my goal).
For example, say the code is:
<div><ruby>
<rb>T</rb><rt>u</rt>
<rb>E</rb><rt>f</rt>
<rb>S</rb><rt>t</rt>
<rb>T</rb><rt>u</rt>
</ruby></div>
I want to make it so that as they move their cursor with the cursor keys, the cursor moves between the letters delimited with the <rb>
tag, "TEST", as if the letters wrapped in <rt>
; and that, whether they have insert active or not, they can overwrite and delete and insert additional regular characters, but their edits will not affect the <rt>
characters (yes, it’s acceptable that if they mess this up, it will look weird, with hanging hiragana with no letters under it, etc: they should ideally be able to come back and add new characters to replace the deleted ones, to fix it up).
In another mode (with shift pressed, or whatever), I hope to make it the opposite: that they can only edit, delete, and move their insertion caret into, the furigana <rt>
characters "uftu", and cannot affect the regular <rb>
characters "TEST".
This gets close:
<div><ruby>
<rb>T</rb><rt contenteditable="false">u</rt>
<rb>E</rb><rt contenteditable="false">f</rt>
<rb>S</rb><rt contenteditable="false">t</rt>
<rb>T</rb><rt contenteditable="false">u</rt>
</ruby></div>
With contenteditable-false for the <rt>
tags (which could be toggled using JS), the caret cannot be moved up into the furigana text.
Unfortunately, there are still issues:
- backspace still deletes them. I could maybe fix with
onkeydown... preventDefault()
. - selecting them and hitting a key deletes them. I could maybe fix by preventing them from being selected, perhaps using methods from the related question "https://stackoverflow.com/questions/13438391/preventing-text-in-rt-tags-furigana-from-being-selected".
- the cursor keys need to be pressed a second time between each letter, as the caret moves from being to the right of
<rb>
tag to the left of the next. The only fix for this I can see for this is awful: event handlers to detect cursor movement; some way to find where the cursor moved from and to, and decide where I want it to end up; and tricks from "https://stackoverflow.com/questions/6249095/how-to-set-the-caret-cursor-position-in-a-contenteditable-element-div" to actually move the caret. That all sounds nightmare-kludgey, though, and I hope there’s a cleaner way.
Perhaps what I really want is just a way to explicitly specify where the valid "insertion caret points" are, and prevent the caret from going anywhere else?
I’d definitely like to avoid external libs (jQuery et al) if at all possible, though if it makes things way simpler because they’ve already solved this problem, then I’m willing to cave in: no point reinventing this wheel.
2
Answers
Try this
In the below solution, a
locked
class is toggled on thert
orrb
elements depending onshift
key being held down or not.On
keydown
, the class toggling occurs and the keyboard arrows are disabled, because it tries to select the text and causes issues.On
keyup
, the class toggling occurs again and, unless it was an arrow key, the "locked" values are restored from an array.There is an annoying side effect caused by the restoring, where the caret position changes a bit…
But, on my part, I’m stopping here.
You can fix that by looking at
getCaretPosition
in this answer andsetCaretPosition
in this answer.