Программно выбирать текст в контентном HTML-элементе?

В JavaScript можно программно выбирать текст в элементе input или textarea. Вы можете сфокусировать вход с помощью ipt.focus(), а затем выбрать его содержимое с помощью ipt.select(). Вы даже можете выбрать определенный диапазон ipt.setSelectionRange(from,to).

Мой вопрос: есть ли способ сделать это в элементе contenteditable тоже?

Я обнаружил, что могу сделать elem.focus(), чтобы поместить каретку в элемент contenteditable, но в дальнейшем запуск elem.select() не работает (и не работает setSelectionRange). Я не могу найти что-либо в Интернете об этом, но, возможно, я искал неправильную вещь...

Кстати, если это имеет значение, мне нужно только его работать в Google Chrome, так как это для расширения Chrome.

Ответ 1

Если вы хотите выбрать все содержимое элемента (contenteditable или нет) в Chrome, вот как. Это также будет работать в Firefox, Safari 3+, Opera 9+ (возможно, более ранних версиях) и IE 9. Вы также можете создавать подборки до уровня персонажа. Необходимые API-интерфейсы: DOM Range (текущая спецификация DOM Level 2, см. Также MDN) и Selection, который определяется как часть новой спецификации диапазона (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);

Ответ 2

В дополнение к ответ Tim Downs, я сделал решение, которое работает даже в 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;​

Протестировано в IE 8+, Firefox 3+, Opera 9+ и Chrome 2+. Даже я установил его в плагин jQuery:

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();
});

... и кто посмеялся, здесь одинаково для всех наркоманов:

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:

Если вы хотите выбрать всю страницу или содержимое редактируемого региона (помечено знаком contentEditable), вы можете сделать это намного проще, переключившись на designMode и используя document.execCommand:

Там хорошая начальная точка в MDN и littledocumentation.

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

(хорошо работает в IE6 +, Opera 9+, Firefoy 3+, Chrome 2+) http://caniuse.com/#search=execCommand

Ответ 3

Rangy позволяет вам делать этот кросс-браузер с тем же кодом. Rangy - это кросс-браузерная реализация методов DOM для выбора. Он хорошо протестирован и делает это намного менее болезненным. Я отказываюсь дотронуться до контента без него.

Здесь вы можете найти rangy:

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

С rangy в вашем проекте вы всегда можете написать это, даже если браузер IE 8 или ранее и имеет совершенно другой собственный API для выбора:

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

Где "contentEditableNode" - это DOM node, который имеет атрибут contenteditable. Вы можете получить его следующим образом:

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

Или, если jQuery уже является частью вашего проекта, и вы считаете его удобным:

var contentEditable = $('.some-selector')[0];

Ответ 4

Поскольку все существующие ответы касаются элементов div, я объясню, как это сделать с помощью span s.

При выборе диапазона текста в span существует тонкая разница. Чтобы иметь возможность передавать индекс начала и конца текста, вы должны использовать Text node, как описано здесь:

Если startNode является Node типа Text, Comment или CDATASection, то startOffset - это количество символов с начала startNode. Для других типов Node startOffset - это число дочерних узлов между началом 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);

Ответ 5

[Обновлено, чтобы исправить ошибку] ​​

Вот пример, который адаптирован из этого ответа, который, как представляется, хорошо работает в Chrome - Выберите диапазон в 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:

<div id="myText" contenteditable>test</div>

Ответ 6

contenteditable - это просто атрибут, который интерпретируется браузером. Вы можете изменять такие элементы с помощью обычных функций DOM.

Смотрите javascript TextRanges.

Btw, быстрый поиск в SO, дал мне результаты пары.