Selecting text in div

2014-02-07

Or in a span, or any element that doesn't expose a select() method (like input and textarea do). These elements also don't expose the selectionStart range of properties to do the same thing.

This workaround is pretty old, but seems to work solidly in any browser these days.

Code:
var select = (function(s,r){
return function(e){
s.removeAllRanges();
r.selectNodeContents(e);
s.addRange(r);
};
})(getSelection(), document.createRange());

select(element1);
select(element2);

The cleaner version comes from http://stackoverflow.com/questions/6139107/programatically-select-text-in-a-contenteditable-html-element :

Code:
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);

It's just silly that the DOM doesn't expose these methods on any element.

In case you're curious. This works because getSelection returns an object, not the actual selection. It's toString method is "live", meaning it will return whatever the current selection is at the time of calling toString, rather than at the time of calling getSelection. This is kind of counter intuitive to me, but whatever.

Likewise, document.createRange() creates a re-usable object.

It's unfortunate that range.selectNodeContents(e) doesn't return the range value or we could simplify the code. Especially if addRange(r) would also call removeAllRanges() first. Ohwell.

Here's some boilerplate to do selection by index:

Code:
function selectRel(el, start, end) {
// end may be < 0 and is relative to start, start must be >=0
selectAbs(el, start, start + end);
}
function selectAbs(el, start, end){
// end may be 0 < end < start, start must be >=0

var s = getSelection();
s.removeAllRanges();

var r = new Range();
r.setStart(el, 0);
s.addRange(r);

if (typeof start === 'number' && start >= 0) {
if (typeof end === 'number') {
var d = end > start ? 'forward' : 'backward';
end = Math.abs(Math.abs(end) - start); // -5 - 10 = -15, 5 - 10 = -5
} else {
end = 0;
}
} else {
start = 0;
}

// modify api is quite tedious...
while (start--) {
s.modify('move', 'forward', 'character');
}
while (end--) {
s.modify('extend', d, 'character');
}
}