150

In JavaScript, it's possible to programmatically select text in an input or textarea element. You can focus an input with ipt.focus(), and then select its contents with ipt.select(). You can even select a specific range with ipt.setSelectionRange(from,to).

My question is: is there any way to do this in a contenteditable element too?

I found that I can do elem.focus(), to put the caret in a contenteditable element, but subsequently running elem.select() doesn't work (and nor does setSelectionRange). I can't find anything on the web about it, but maybe I'm searching for the wrong thing...

By the way, if it makes any difference, I only need it to work in Google Chrome, as this is for a Chrome extension.

Cœur
  • 37,241
  • 25
  • 195
  • 267
callum
  • 34,206
  • 35
  • 106
  • 163
  • 1
    Modern [answer](https://stackoverflow.com/a/61217786/8112776): **`window.getSelection().selectAllChildren( element );`** ([documentation](https://developer.mozilla.org/docs/Web/API/Selection/selectAllChildren)) – ashleedawg Feb 19 '22 at 10:38

6 Answers6

208

If you want to select all the content of an element (contenteditable or not) in Chrome, here's how. This will also work in Firefox, Safari 3+, Opera 9+ (possibly earlier versions too) and IE 9. You can also create selections down to the character level. The APIs you need are DOM Range (current spec is DOM Level 2, see also MDN) and Selection, which is being specified as part of a new Range spec (MDN docs).

function selectElementContents(el) {
    var range = document.createRange();
    range.selectNodeContents(el);
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
}

var el = document.getElementById("foo");
selectElementContents(el);
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 17
    For extra compatibilty you should call `selectElementContents()` in a `setTimeout()` or `requestAnimationFrame()` if called from an `onfocus`. See http://jsfiddle.net/rudiedirkx/MgASG/1/show/ – Rudie Apr 04 '13 at 23:00
  • @Dylan: I'm not sure: the question mentions that the OP is already using `focus()`. – Tim Down Jun 14 '13 at 08:48
  • 4
    @Rudie *Compatibility* for which application? – yckart Jun 03 '16 at 11:10
  • Works great on desktop. On mobile browsers, does not work. No selection made. Tried Safari and Chrome on iPhone iOS 11. – campbell Dec 11 '17 at 22:25
  • 1
    @campbell: It does work on Safari at least on iOS, **provided you already have a selection**. Otherwise no, the browser simply does not allow JavaScript to show a selection, presumably for user experience reasons. – Tim Down Dec 12 '17 at 13:35
  • Doesn't work if the element is `display:none;` So i just moved the element off screen...Thanks for the code! – Barrard Jul 10 '18 at 08:36
  • There's a faster way using `selectAllChildren()`. See my answer below. –  Apr 14 '20 at 21:47
  • @Catalin `selectAllChildren()` was around in 2011 and I would have been aware of it, so why I didn't suggest it is unclear. I'm guessing it may have had some compatibility issues. I agree it's safe to use it now. – Tim Down Apr 14 '20 at 23:33
36

In addition to Tim Downs answer, i made a solution that work even in oldIE:

var selectText = function() {
  var range, selection;
  if (document.body.createTextRange) {
    range = document.body.createTextRange();
    range.moveToElementText(this);
    range.select();
  } else if (window.getSelection) {
    selection = window.getSelection();
    range = document.createRange();
    range.selectNodeContents(this);
    selection.removeAllRanges();
    selection.addRange(range);
  }
};

document.getElementById('foo').ondblclick = selectText;​

Tested in IE 8+, Firefox 3+, Opera 9+, & Chrome 2+. Even I've set it up into a jQuery plugin:

jQuery.fn.selectText = function() {
  var range, selection;
  return this.each(function() {
    if (document.body.createTextRange) {
      range = document.body.createTextRange();
      range.moveToElementText(this);
      range.select();
    } else if (window.getSelection) {
      selection = window.getSelection();
      range = document.createRange();
      range.selectNodeContents(this);
      selection.removeAllRanges();
      selection.addRange(range);
    }
  });
};

$('#foo').on('dblclick', function() {
  $(this).selectText();
});

...and who's intereseted in, here's the same for all coffee-junkies:

jQuery.fn.selectText = ->
  @each ->
    if document.body.createTextRange
      range = document.body.createTextRange()
      range.moveToElementText @
      range.select()
    else if window.getSelection
      selection = window.getSelection()
      range = document.createRange()
      range.selectNodeContents @
      selection.removeAllRanges()
      selection.addRange range
    return

Update:

If you want to select the entire page or contents of an editable region (flagged with contentEditable), you can do it much simpler by switching to designMode and using document.execCommand:

There's a good starting point at MDN and a littledocumentation.

var selectText = function () {
  document.execCommand('selectAll', false, null);
};

(works well in IE6+, Opera 9+, Firefoy 3+, Chrome 2+) http://caniuse.com/#search=execCommand

Community
  • 1
  • 1
yckart
  • 32,460
  • 9
  • 122
  • 129
17

The modern way of doing things is like this. More details on MDN

document.addEventListener('dblclick', (event) => {
  window.getSelection().selectAllChildren(event.target)
})
<div contenteditable="true">Some text</div>
  • Thanks, this works great! Fwiw, that MDN page marks this technology as experimental. But it works in the current version of Chrome and FF in June 2020. – JohnK Jun 17 '20 at 09:09
  • Selecting only on mouse events is not feasible. User can select text with keyboard, too. – Gevorg Hakobyan Nov 12 '22 at 19:05
11

Since all of the existing answers deal with div elements, I'll explain how to do it with spans.

There is a subtle difference when selecting a text range in a span. In order to be able to pass the text start and end index, you have to use a Text node, as described here:

If the startNode is a Node of type Text, Comment, or CDATASection, then startOffset is the number of characters from the start of startNode. For other Node types, startOffset is the number of child nodes between the start of the startNode.

var e = document.getElementById("id of the span element you want to select text in");
var textNode = e.childNodes[0]; //text node is the first child node of a span

var r = document.createRange();
var startIndex = 0;
var endIndex = textNode.textContent.length;
r.setStart(textNode, startIndex);
r.setEnd(textNode, endIndex);

var s = window.getSelection();
s.removeAllRanges();
s.addRange(r);
Domysee
  • 12,718
  • 10
  • 53
  • 84
  • Really should be: ```r.setStart(e.firstChild,0); r.setEnd(e.lastChild,e.lastChild.textContent.length);``` Of course you should check that e.firstChild is actually not null. – Yorick Sep 13 '17 at 15:49
  • 3
    There is no difference between making a selection in a `
    ` and a `` element. At least, not as you describe.
    – Tim Down Dec 12 '17 at 13:39
  • 2
    There are differences between div and span, in some cases a solution for div doesn't work right in span. For example if you select text programmatically with div solution then paste new content, it will replace not the whole text, only a part and there are differences between chrome and firefox – neosonne Nov 28 '19 at 07:12
6

Rangy allows you to do this cross-browser with the same code. Rangy is a cross-browser implementation of the DOM methods for selections. It is well tested and makes this a lot less painful. I refuse to touch contenteditable without it.

You can find rangy here:

http://code.google.com/p/rangy/

With rangy in your project, you can always write this, even if the browser is IE 8 or earlier and has a completely different native API for selections:

var range = rangy.createRange();
range.selectNodeContents(contentEditableNode);
var sel = rangy.getSelection();
sel.removeAllRanges();
sel.addRange(range);

Where "contentEditableNode" is the DOM node that has the contenteditable attribute. You might fetch it like this:

var contentEditable = document.getElementById('my-editable-thing');

Or if jQuery is part of your project already and you find it convenient:

var contentEditable = $('.some-selector')[0];
Tom Boutell
  • 7,281
  • 1
  • 26
  • 23
2

[Updated to fix mistake]

Here is an example that is adapted from this answer that appears to work well in Chrome - Select range in contenteditable div

var elm = document.getElementById("myText"),
    fc = elm.firstChild,
    ec = elm.lastChild,
    range = document.createRange(),
    sel;
elm.focus();
range.setStart(fc,1);
range.setEnd(ec,3);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);

HTML is:

<div id="myText" contenteditable>test</div>
Community
  • 1
  • 1
patorjk
  • 2,164
  • 1
  • 20
  • 30