¿Seleccionar texto mediante programación en un elemento HTML satisfactorio?


119

En JavaScript, es posible seleccionar texto mediante programación en un elemento inputo textarea. Puede enfocar una entrada con ipt.focus()y luego seleccionar su contenido con ipt.select(). Incluso puede seleccionar un rango específico con ipt.setSelectionRange(from,to).

Mi pregunta es: ¿hay alguna forma de hacer esto también en un contenteditableelemento?

Descubrí que puedo hacer elem.focus(), poner el símbolo de intercalación en un contenteditableelemento, pero posteriormente ejecutarlo elem.select()no funciona (ni tampoco setSelectionRange). No puedo encontrar nada en la web al respecto, pero tal vez estoy buscando algo equivocado ...

Por cierto, si hace alguna diferencia, solo necesito que funcione en Google Chrome, ya que es para una extensión de Chrome.

Respuestas:


170

Si desea seleccionar todo el contenido de un elemento (contento o no) en Chrome, aquí le mostramos cómo. Esto también funcionará en Firefox, Safari 3+, Opera 9+ (posiblemente versiones anteriores también) e IE 9. También puede crear selecciones hasta el nivel del personaje. Las API que necesita son DOM Range (la especificación actual es DOM Level 2 , consulte también MDN ) y Selection, que se especifica como parte de una nueva especificación de Range ( documentos 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
Para mayor compatibilidad, debe llamar selectElementContents()a setTimeout()o requestAnimationFrame()si se llama desde un onfocus. Ver jsfiddle.net/rudiedirkx/MgASG/1/show
Rudie

@Dylan: No estoy seguro: la pregunta menciona que el OP ya está usando focus().
Tim Down

4
¿ Compatibilidad de @Rudie para qué aplicación?
yckart

Funciona muy bien en el escritorio. En navegadores móviles, no funciona. No se hizo ninguna selección. Probé Safari y Chrome en iPhone iOS 11.
campbell

1
@campbell: funciona en Safari al menos en iOS, siempre que ya tenga una selección . De lo contrario, no, el navegador simplemente no permite que JavaScript muestre una selección, presumiblemente por razones de experiencia del usuario.
Tim Down

34

Además de la respuesta de Tim Downs , hice una solución que funciona incluso en 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;​

Probado en IE 8+, Firefox 3+, Opera 9+ y Chrome 2+. Incluso lo configuré en un complemento 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();
});

... y a quién le interesa, esto es lo mismo para todos los adictos al café:

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

Actualizar:

Si desea seleccionar la página completa o el contenido de una región editable (marcada con contentEditable), puede hacerlo mucho más simple cambiando ay designModeusandodocument.execCommand :

Hay un buen punto de partida en MDN y una pequeña documentación .

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

(funciona bien en IE6 +, Opera 9+, Firefoy 3+, Chrome 2+) http://caniuse.com/#search=execCommand


10

Dado que todas las respuestas existentes tratan con divelementos, explicaré cómo hacerlo con spans.

Existe una sutil diferencia al seleccionar un rango de texto en un span. Para poder pasar el índice de inicio y finalización del texto, debe usar un Textnodo, como se describe aquí :

Si startNode es un Nodo de tipo Text, Comment o CDATASection, startOffset es el número de caracteres desde el inicio de startNode. Para otros tipos de nodos, startOffset es el número de nodos secundarios entre el inicio de 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);

Realmente debería ser: r.setStart(e.firstChild,0); r.setEnd(e.lastChild,e.lastChild.textContent.length); Por supuesto, debería comprobar que e.firstChild en realidad no sea nulo.
Yorick

2
No hay diferencia entre hacer una selección en un <div>y un <span>elemento. Al menos, no como lo describe.
Tim Down

Existen diferencias entre div y span, en algunos casos una solución para div no funciona correctamente en span. Por ejemplo, si selecciona texto mediante programación con la solución div y luego pega contenido nuevo, no reemplazará todo el texto, solo una parte y hay diferencias entre Chrome y
Firefox

6

Rangy te permite hacer este cross-browser con el mismo código. Rangy es una implementación de varios navegadores de los métodos DOM para las selecciones. Está bien probado y hace que esto sea mucho menos doloroso. Me niego a tocar contenteditable sin él.

Puedes encontrar rangy aquí:

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

Con rangy en su proyecto, siempre puede escribir esto, incluso si el navegador es IE 8 o anterior y tiene una API nativa completamente diferente para las selecciones:

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

Donde "contentEditableNode" es el nodo DOM que tiene el atributo contenteditable. Puede buscarlo así:

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

O si jQuery ya es parte de su proyecto y lo encuentra conveniente:

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

El proyecto Rangy se trasladó a Github ahora: github.com/timdown/rangy
tanius

4

La forma moderna de hacer las cosas es así. Más detalles sobre MDN

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


Gracias, esto funciona muy bien. Fwiw, esa página de MDN marca esta tecnología como experimental. Pero funciona en la versión actual de Chrome y FF en junio de 2020.
JohnK

2

[Actualizado para corregir el error]

Aquí hay un ejemplo que está adaptado de esta respuesta que parece funcionar bien en Chrome: seleccione el rango en div contenteditable

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 es:

<div id="myText" contenteditable>test</div>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.