Why Is My Contenteditable caret Jumping to the End in Chrome?
So, I've got a slightly cleaner implementation of your approach, which relies on the input
event that is triggered when a contenteditable
field is updated. This event is not supported by IE, but it looks like contenteditable
is pretty wonky in IE anyways.
It basically injects elements around every element marked as contenteditable="false"
. It could probably be done on initial load rather than just on the input
event, but I figured you might have ways of injecting more span
's to the region, so input
should account for this.
Limitations include some weird behavior surrounding the arrow keys, as you've seen yourself. Mostly what I've seen is that the cursor maintains its proper position when you move backward through the spans, but not when you move forward.
JSFiddle Link
Javascript
$('#main').on('input', function() {
var first = true;
$(this).contents().each(function(){
if ($(this).is('[contenteditable=false]')) {
$("<i class='wrapper'/>").insertAfter(this);
if (first) {
$("<i class='wrapper'/>").insertBefore(this);
first = false
}
}
});
}).trigger('input');
CSS
div.main {
width: 400px;
height: 250px;
border: solid 1px black;
}
div.main span {
width:40px;
background-color: red;
border-radius: 5px;
color: white;
cursor: pointer;
}
i.wrapper {
font-style: normal;
}
In my case also, when I was trying to edit content editable span placed inside div, It was giving the same problem ,But when appended \n after content still problem occurred if we tried to erase last digit so I appended "\n" before and after content like and now it is working fine,thanks to @Mati Horovitz.
var html = `<div id="main" contenteditable="true">' \n ${content} \n </div>`
For Angular people, you can do
<span (input)="saveContent($event.target.innerHTML)" [attr.contenteditable]="isEditable">{{'\n'+content+'\n'}}</span >
I don't know why this happens, but I had the feeling it has something to do with the sizing of the <div>
, so I tried playing with the display
property. After setting it to inline-block
and playing a little with the text I found that the issue is gone after I make some edits to it, specifically adding a new line.
I saw that, for some reason, the <br/>
tag is kept in div.main
after I delete my new line, but the appearance of the <div>
and the way it responds to arrow keys is the same as if there is no new line in it.
So I restarted the fiddle with the CSS change and a <br/>
tag in div.main
and viola!
So to conclude:
- Add
display: inline-block
todiv.main
- add a
<br/>
at the end ofdiv.main
JSFiddle Link
The problem is that your contenteditable
element is a div
, which by default is display: block
. This is what causes your caret position problem. This can be fixed by making the outermost div
non-editable and adding a new editable div
that will be treated as inline-block
.
The new HTML would have a new div
just inside the outer one (and the corresponding closing tag at the end):
<div id="main" class="main"><div id="editable" contenteditable="true">...
And add this to your CSS:
div#editable {
display: inline-block;
}
For the sake of seeing the caret better when it is between span
elements, I've also added margin: 2px
to the rule for div.main span
in the CSS but this is not necessary to prevent the caret jumping issue reported in the question.
Here is a fiddle.
As you've started discovering, contenteditable
is handled inconsistently across browsers. A few years back, I started working on a project (in-browser XML editor) where I thought contenteditable
would make everything easier. Then as I developed the application, I soon found myself taking over the functions that I thought contenteditable
would give me for free. Today, the only thing contenteditable
give me is that it turns on keyboard events on elements I want to edit. Everything else, including caret movement and caret display, is managed by custom code.