Програмно вибрати текст у вмістому 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 (поточна специфікація - рівень DOM 2 , див. Також MDN ) та селекція, яка задається як частина нової специфікації діапазону ( документи 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
Rudie

@Dylan: Я не впевнений: у питанні йдеться про те, що ОП вже використовується focus().
Тім Даун

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


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

Дійсно має бути: r.setStart(e.firstChild,0); r.setEnd(e.lastChild,e.lastChild.textContent.length); Звичайно, ви повинні перевірити, що e.firstChild насправді не є нульовим.
Йорк

2
Немає різниці між вибором в a <div>і <span>елементі. Принаймні, не так, як ви описуєте.
Тім Даун

Існують відмінності між div та span, в деяких випадках рішення для div не працює прямо в проміжку. Наприклад, якщо ви вибираєте текст програмно за допомогою рішення div, потім вставляєте новий вміст, він замінить не весь текст, а лише частину, а між хромом та firefox є відмінності
neosonne

6

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, який має атрибут 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 - Виберіть діапазон у вмістому розділі

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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.