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

119

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

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

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

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

Каллум
источник

Ответы:

170

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

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);
Тим Даун
источник
15
Для дополнительной совместимости вам следует позвонить selectElementContents()в setTimeout()или, requestAnimationFrame()если звонят из onfocus. См. Jsfiddle.net/rudiedirkx/MgASG/1/show
Руди
@Dylan: Я не уверен: в вопросе упоминается, что OP уже используется focus().
Tim Down
4
@Rudie Совместимость для какого приложения?
yckart
Отлично работает на рабочем столе. В мобильных браузерах не работает. Выбор не сделан. Пробовал Safari и Chrome на iPhone IOS 11.
Кемпбелл
1
@campbell: Он работает в Safari, по крайней мере, на iOS, если у вас уже есть выбор . В противном случае нет, браузер просто не позволяет JavaScript отображать выбор, предположительно по причинам, связанным с пользовательским интерфейсом.
Тим Даун
34

В дополнение к ответу Тима Даунса я сделал решение, которое работает даже в 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

Обновить:

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

В MDN есть хорошая отправная точка и немного документации .

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

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

yckart
источник
10

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

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

Если startNode является узлом типа Text, Comment или CDATASection, то startOffset - это количество символов от начала startNode. Для других типов узлов 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);
Domysee
источник
На самом деле должно быть: r.setStart(e.firstChild,0); r.setEnd(e.lastChild,e.lastChild.textContent.length); конечно, вы должны проверить, что e.firstChild на самом деле не null.
Йорик
2
Нет никакой разницы между выделением элементов <div>и <span>элементов. По крайней мере, не так, как вы описываете.
Тим Даун
Между div и span есть различия, в некоторых случаях решение для div не работает правильно в span. Например, если вы программно выделите текст с помощью решения div, а затем вставите новый контент, он заменит не весь текст, а только его часть, и есть различия между chrome и firefox
neosonne
6

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

Вы можете найти здесь:

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 с атрибутом contenteditable. Вы можете получить его так:

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

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

var contentEditable = $('.some-selector')[0];
Том Бутелл
источник
Проект Rangy теперь перемещен на Github: github.com/timdown/rangy
tanius
4

Современный способ работы такой. Подробнее о MDN

document.addEventListener('dblclick', (event) => {
  window.getSelection().selectAllChildren(event.target)
})
<div contenteditable="true">Some text</div>


источник
Спасибо, отлично работает! Fwiw, эта страница MDN помечает эту технологию как экспериментальную. Но он работает в текущей версии Chrome и FF в июне 2020 года.
JohnK,
2

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

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