contenteditable, set caret at the end of the text (cross-browser)
Solution 1
The following function will do it in all major browsers:
function placeCaretAtEnd(el) {
el.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}
placeCaretAtEnd( document.querySelector('p') );
p{ padding:.5em; border:1px solid black; }
<p contentEditable>foo bar </p>
Placing the caret at the start is almost identical: it just requires changing the Boolean passed into the calls to collapse()
. Here's an example that creates functions for placing the caret at the start and at the end:
function createCaretPlacer(atStart) {
return function(el) {
el.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(atStart);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(atStart);
textRange.select();
}
};
}
var placeCaretAtStart = createCaretPlacer(true);
var placeCaretAtEnd = createCaretPlacer(false);
Solution 2
Unfortunately Tim's excellent answer worked for me only for placing at the end, for placing at the start I had to modify it slightly.
function setCaret(target, isStart) {
const range = document.createRange();
const sel = window.getSelection();
if (isStart){
const newText = document.createTextNode('');
target.appendChild(newText);
range.setStart(target.childNodes[0], 0);
}
else {
range.selectNodeContents(target);
}
range.collapse(isStart);
sel.removeAllRanges();
sel.addRange(range);
target.focus();
target.select();
}
Not sure though if focus()
and select()
are actually needed.
Solution 3
This (live) example shows a short simple function, setCaretAtStartEnd
, which takes two arguments; A (editable) node to place the caret at & a Boolean indicating where to place it (start or end of the node)
const editableElm = document.querySelector('[contenteditable]');
document.querySelectorAll('button').forEach((elm, idx) =>
elm.addEventListener('click', () => {
editableElm.focus()
setCaretAtStartEnd(editableElm, idx)
})
)
function setCaretAtStartEnd( node, atEnd ){
const sel = document.getSelection();
node = node.firstChild;
if( sel.rangeCount ){
['Start', 'End'].forEach(pos =>
sel.getRangeAt(0)["set" + pos](node, atEnd ? node.length : 0)
)
}
}
[contenteditable]{ padding:5px; border:1px solid; }
<h1 contenteditable>Place the caret anywhere</h1>
<br>
<button>Move caret to start</button>
<button>Move caret to end</button>
Admin
Updated on December 04, 2020Comments
-
Admin over 3 years
output in Chrome:
<div id="content" contenteditable="true" style="border:1px solid #000;width:500px;height:40px;"> hey <div>what's up?</div> <div> <button id="insert_caret"></button>
I believe in FF it would look something like this:
hey <br /> what's up?
and in IE:
hey <p>what's up?</p>
unfortunately, there is no nice way of making it so that every browser inserts a
<br />
instead of a div- or p-tag, or at least I couldn't find anything online.
ANYWAY, what I am trying to do now is, when I hit the button, I want the caret to be set at the end of the text, so it should look something like this:
hey what's up?|
any way to do this so it works in all browser?
example:
$(document).ready(function() { $('#insert_caret').click(function() { var ele = $('#content'); var length = ele.html().length; ele.focus(); //set caret -> end pos } }
-
Anh Tú about 7 yearsrange = goog.dom.Range.createFromNodeContents(el); what's goog.dom?
-
dotnethaggis almost 7 yearsYou also introduced an external library. If you don't think focus and select is required why not comment out the lines and test it?
-
dimid almost 7 yearsThanks, removed jquery. I remember I had some contradicting results, I'll try to isolate them again.
-
whitneyland over 6 yearsDoes not work with Chrome because createTextRange is not a standard function. See stackoverflow.com/a/41743191/700206.
-
Tim Down over 6 years@Lee: It works fine in Chrome, which supports
window.getSelection
anddocument.createRange
. ThecreateTextRange
branch is for old versions of Internet Explorer. -
w.stoettinger over 6 yearsat the time of writing
window.getSelection
is not supported by 0.29% of all browsers (IE>8). see: caniuse.com/#search=window.getSelection -
shinobi about 6 years@TimDown this works. +1. But can you also explain the code by adding comments please? Would be great
-
RobbTe almost 6 yearsAnyone knows how to set the caret to the end (use this code) when trying to target an iframe > body element? The body element has
editablecontent="true"
...?placeCaretAtEnd(this);
won't work for me. -
David Beneš over 5 yearsThis worked for me. @AnhTú: goog.dom is a namespace provided by closure-library.
-
vsync over 5 years@TimDown - Took the liberty adding a demo. I face a difficult problem where such a solution doesn't work when there's a trailing space. I saw you faced a similar problem, some years back in one of the github repos. any insight on the problem?
-
Alpesh Prajapati over 5 years@TimDown This solution is working chrome, but in my case it does not work in firefox. Can you please help ?..Issue in firefox is like cursor is pointing to last character but when i type a single character, cursor jumps on to first character and writing after will add characters from right to left(Islamic language)..
-
Tim Down over 5 years@AlpeshPrajapati: Do you have a demo page I can look at?
-
Alpesh Prajapati over 5 years@TimDown Thank you very much for your response. But now I think I got the issue why it was like that. It was data-binding issue and updating at the same time. Thanks to the blog: mutuallyhuman.com/blog/2018/05/03/… ...Read the section "Asynchronous saves and data refreshes to the server"
-
webprogrammer over 4 yearsDoes it work with nested
hey <p>what's up?</p>
? Where the caret is put: right after "?" mark or after closing "</p>" tag? -
user1432181 over 3 yearsThis seems to work ok : jsfiddle.net/Abeeee/7me8dkpb
-
Tim Down over 3 years@user1432181: You've got a typo in "contenteditable" and you can't call focus() on an element within the editable content. Here's a working example: jsfiddle.net/c0md8u5n
-
ashleedawg over 2 yearsHere's the exact same function, compressed:
function placeCaretAtEnd(el){el.focus();var u="undefined",w=window,r,s,t;if(typeof w.getSelection!=u&&typeof document.createRange!=u){r=document.createRange();r.selectNodeContents(el);r.collapse(false);s=w.getSelection();s.removeAllRanges();s.addRange(r)}else if(typeof document.body.createTextRange!=u){t=document.body.createTextRange();t.moveToElementText(el);t.collapse(false);t.select()}}
. Works great for me, everywhere. -
ashleedawg over 2 yearsI was about to correct another question that used this code, to cite this answer as the source, but then realized it's been quoted several times, so I'm not sure whether or not @TimDown wrote it and don't know who to credit... alas, I tried.
-
ashleedawg over 2 years
focus()
places the caret at the beginning for me with no additional code required -
Tim Down over 2 years@ashleedawg: I definitely wrote this code.