Selezionare a livello di codice il testo in un elemento HTML modificabile con contenuto?


119

In JavaScript, è possibile selezionare a livello di codice il testo in un elemento inputo textarea. Puoi mettere a fuoco un input con ipt.focus(), quindi selezionarne il contenuto con ipt.select(). Puoi anche selezionare un intervallo specifico con ipt.setSelectionRange(from,to).

La mia domanda è: esiste un modo per farlo anche in un contenteditableelemento?

Ho scoperto che posso fare elem.focus(), mettere il cursore in un contenteditableelemento, ma successivamente l'esecuzione elem.select()non funziona (e non funziona setSelectionRange). Non riesco a trovare nulla sul web al riguardo, ma forse sto cercando la cosa sbagliata ...

A proposito, se fa qualche differenza, ho solo bisogno che funzioni in Google Chrome, poiché si tratta di un'estensione di Chrome.

Risposte:


170

Se vuoi selezionare tutto il contenuto di un elemento (contenteditable o no) in Chrome, ecco come. Funzionerà anche in Firefox, Safari 3+, Opera 9+ (forse anche versioni precedenti) e IE 9. Puoi anche creare selezioni fino al livello del personaggio. Le API di cui hai bisogno sono DOM Range (la specifica corrente è DOM Level 2 , vedi anche MDN ) e Selection, che viene specificata come parte di una nuova specifica Range ( documenti 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
Per maggiore compatibilità dovresti chiamare selectElementContents()in un setTimeout()o requestAnimationFrame()se chiamato da un onfocus. Vedi jsfiddle.net/rudiedirkx/MgASG/1/show
Rudie

@ Dylan: non sono sicuro: la domanda menziona che l'OP sta già utilizzando focus().
Tim Down

4
@Rudie Compatibility per quale applicazione?
yckart

Funziona alla grande sul desktop. Sui browser mobili, non funziona. Nessuna selezione effettuata. Ho provato Safari e Chrome su iPhone iOS 11.
campbell

1
@campbell: funziona su Safari almeno su iOS, a condizione che tu abbia già una selezione . Altrimenti no, il browser semplicemente non consente a JavaScript di mostrare una selezione, presumibilmente per motivi di esperienza dell'utente.
Tim Down

34

Oltre alla risposta di Tim Downs , ho realizzato una soluzione che funziona anche in 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;​

Testato in IE 8+, Firefox 3+, Opera 9+ e Chrome 2+. Anche io l'ho configurato in un plugin 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();
});

... e chi è interessato, ecco lo stesso per tutti i drogati di caffè:

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

Aggiornare:

Se desideri selezionare l'intera pagina o il contenuto di un'area modificabile (contrassegnata con contentEditable), puoi farlo molto più semplice passando a designModee utilizzandodocument.execCommand :

C'è un buon punto di partenza in MDN e una piccola documentazione .

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

(funziona bene in IE6 +, Opera 9+, Firefoy 3+, Chrome 2+) http://caniuse.com/#search=execCommand


10

Poiché tutte le risposte esistenti riguardano divelementi, spiegherò come farlo con spans.

C'è una sottile differenza quando si seleziona un intervallo di testo in un file span. Per poter passare l'indice iniziale e finale del testo, è necessario utilizzare un Textnodo, come descritto qui :

Se startNode è un nodo di tipo Text, Comment o CDATASection, startOffset è il numero di caratteri dall'inizio di startNode. Per altri tipi di nodo, startOffset è il numero di nodi figlio tra l'inizio di 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);

In realtà dovrebbe essere: r.setStart(e.firstChild,0); r.setEnd(e.lastChild,e.lastChild.textContent.length); Ovviamente dovresti controllare che e.firstChild non sia effettivamente nullo.
Yorick

2
Non c'è differenza tra fare una selezione in un <div>e un <span>elemento. Almeno, non come descrivi.
Tim Down

Esistono differenze tra div e span, in alcuni casi una soluzione per div non funziona correttamente in span. Ad esempio, se selezioni il testo in modo programmatico con la soluzione div e incolli il nuovo contenuto, non sostituirà l'intero testo, solo una parte e ci sono differenze tra chrome e firefox
neosonne

6

Rangy ti consente di eseguire questa operazione cross-browser con lo stesso codice. Rangy è un'implementazione cross-browser dei metodi DOM per le selezioni. È ben testato e lo rende molto meno doloroso. Mi rifiuto di toccare contenteditable senza di essa.

Puoi trovare rangy qui:

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

Con rangy nel tuo progetto, puoi sempre scrivere questo, anche se il browser è IE 8 o precedente e ha un'API nativa completamente diversa per le selezioni:

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

Dove "contentEditableNode" è il nodo DOM che ha l'attributo contenteditable. Potresti recuperarlo in questo modo:

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

O se jQuery fa già parte del tuo progetto e lo trovi conveniente:

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

Il progetto Rangy si è spostato su Github ora: github.com/timdown/rangy
tanius

4

Il modo moderno di fare le cose è così. Maggiori dettagli su MDN

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


Grazie, funziona benissimo! Fwiw, quella pagina MDN contrassegna questa tecnologia come sperimentale. Ma funziona nella versione corrente di Chrome e FF nel giugno 2020.
JohnK

2

[Aggiornato per correggere l'errore]

Ecco un esempio adattato da questa risposta che sembra funzionare bene in Chrome: seleziona l'intervallo in 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 è:

<div id="myText" contenteditable>test</div>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.