In questo articolo affronteremo la selezione nel documento, così come quella nei campi dei form, come gli <input>
.
JavaScript può accedere a una selezione esistente, selezionare/deselezionare nodi DOM, interamente o parzialmente, rimuovere il contenuto selezionato dal documento, racchiuderlo in un tag e così via.
Puoi trovare degli script già pronti per le azioni più comuni, alla fine del capitolo, nel “Riepilogo”. Forse faranno fronte alle tue esigenze, ma otterrai molte più informazioni leggendo tutto il capitolo. Gli oggetti sottostanti Range
e Selection
sono di facile comprensione, e potrai quindi farne ciò che vuoi senza dover utilizzare script già pronti.
Range
Il concetto alla base della selezione è il Range, il quale definisce una coppia di “punti limite”: inizio e fine del range.
Un oggetto Range
viene creato senza parametri:
let range = new Range();
Possiamo quindi impostare i limiti della nostra selezione usando range.setStart(node, offset)
e range.setEnd(node, offset)
.
Come puoi immaginare, più avanti useremo gli oggetti Range
per la selezione, ma prima proviamo a creare alcuni di questi oggetti.
Selezione parziale del testo
la cosa interessante è che il primo argomento node
in entrambi i metodi, può essere sia uno nodo di testo che uno nodo-elemento, ed l’interpretazione del secondo elemento dipende da questo.
Se node
è un nodo di testo, allora offset
deve essere la posizione nel testo.
Ad esempio, dato l’elemento <p>Hello</p>
, possiamo creare il range contenente le lettere “ll” nel seguente modo:
<p id="p">Hello</p>
<script>
let range = new Range();
range.setStart(p.firstChild, 2);
range.setEnd(p.firstChild, 4);
// toString di un range, restituisce il suo contenuto come testo
console.log(range); // ll
</script>
Qui prendiamo il primo elemento figlio di <p>
(che è il nodo di testo) e specifichiamo la posizione del testo al suo interno:
Selezione nei nodi elemento
In alternativa, se node
è un nodo elemento, allora offset
deve essere il numero dell’elemento figlio.
Questo è utile per creare selezioni che contengono nodi completi, senza limitarsi al solo testo contenuto.
Ad esempio, qui abbiamo una parte di un documento più complesso:
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
Ecco la struttura del DOM con sia nodi testo che nodi elemento:
Creiamo un range per "Example: <i>italic</i>"
.
Come possiamo vedere, questa frase è composta esattamente dal primo e dal secondo figlio di <p>
, con indici 0
e 1
:
-
L’inizio contiene
<p>
comenodo
genitore, e0
come offset. Quindi possiamo impostarlo conrange.setStart(p, 0)
. -
La fine contiene anch’essa
<p>
comenodo
genitore, ma2
come offset (specifica il range fino aoffset
, ma senza includerlo). Quindi possiamo impostarlo conrange.setEnd(p, 2)
.
Eseguendo la demo potrai vedere il testo che viene selezionato:
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<script>
let range = new Range();
range.setStart(p, 0);
range.setEnd(p, 2);
// toString di un range restituisce il suo contenuto come testo senza i tags
console.log(range); // Example: italic
// applichiamo questo range per la selezione del documento (verrà spiegato in seguito)
document.getSelection().addRange(range);
</script>
Ecco esempio più flessibile, dove provare più varianti:
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
From <input id="start" type="number" value=1> – To <input id="end" type="number" value=4>
<button id="button">Click to select</button>
<script>
button.onclick = () => {
let range = new Range();
range.setStart(p, start.value);
range.setEnd(p, end.value);
// applica la selezione, verrà spiegato in seguito
document.getSelection().removeAllRanges();
document.getSelection().addRange(range);
};
</script>
Ad esempio, la selezione all’interno della stesso <p>
da offset 1
a 4
, ci restituisce il range <i>italic</i> and <b>bold</b>
:
Non dobbiamo necessariamente usare lo stesso nodo in setStart
e setEnd
. Un range può spaziare attraverso un serie di nodi non necessariamente correlati. La sola cosa che importa è che la fine sia effettivamente dopo l’inizio.
Selezionare porzioni più estese
Creiamo una selezione più estesa a partire dal nostro esempio, in questo modo:
Sappiamo già come farlo. Abbiamo solo bisogno di impostare l’inizio e la fine come offset relativo nei nodi testuali.
Dobbiamo creare un range che:
- cominci dalla posizione 2 in
<p>
primo figlio (prendendo tutto tranne le prime due lettere di "Example: ") - finisca alla posizione 3 in
<b>
primo figlio (prendendo le prime tre lettere di “bold”, e nient’altro):
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<script>
let range = new Range();
range.setStart(p.firstChild, 2);
range.setEnd(p.querySelector('b').firstChild, 3);
console.log(range); // ample: italic and bol
// usa questo range per la selezione (spiegato dopo)
window.getSelection().addRange(range);
</script>
Come puoi vedere, è abbastanza facile creare una selezione di qualsiasi cosa ci serva.
Se vogliamo selezionare nodi completi, possiamo passare gli elementi in setStart / setEnd
. Altrimenti, possiamo lavorare a livello di testo.
proprietà di range
L’oggetto range che abbiamo creato nell’esempio ha le seguenti proprietà:
startContainer
,startOffset
– nodo e offset di inizio,- nell’esempio sopra: primo nodo testuale all’interno di
<p>
e2
.
- nell’esempio sopra: primo nodo testuale all’interno di
endContainer
,endOffset
– nodo e offset della fine,- nell’esempio sopra: primo nodo testuale all’interno di
<b>
e3
.
- nell’esempio sopra: primo nodo testuale all’interno di
collapsed
– booleano,true
se il range comincia e finisce nello stesso punto (quindi non c’è contenuto nel range),- nell’esempio sopra:
false
- nell’esempio sopra:
commonAncestorContainer
– il più vicino genitore tra tutti i nodi all’interno del range,- nell’esempio sopra:
<p>
- nell’esempio sopra:
Metodi di range
Ci sono una serie di metodi utili per manipolare i range.
Abbiamo già visto setStart
e setEnd
, di seguito alcuni metodi utili agli stessi scopi.
Impostare l’inizio del range:
setStart(node, offset)
imposta l’inizio: alla posizioneoffset
delnode
setStartBefore(node)
imposta l’inizio: appena prima dinode
setStartAfter(node)
imposta l’inizio: appena dopo dinode
Impostare la fine del range (metodi simili):
setEnd(node, offset)
imposta la fine: alla posizioneoffset
nelnode
setEndBefore(node)
imposta la fine: appena prima dinode
setEndAfter(node)
imposta la fine: appena doponode
Tecnicamente, setStart/setEnd
può fare tutto, ma più metodi possono rivelarsi utili.
Come visto, node
può essere sia un nodo testuale che un nodo elemento: per i nodi testuali offset
salta il numero di caratteri specificato, mentre per i nodi elemento salta i nodi figlio specificati.
Altri metodi per creare ranges:
selectNode(node)
Imposta un range per selezionare l’interonodo
.selectNodeContents(node)
Imposta un range per selezionare l’intero contenuto delnodo
.collapse(toStart)
setoStart=true
impostaend=start
, altrimenti impostastart=end
, collassando così il range.cloneRange()
crea un nuovo range con lo stesso inizio e fine
metodi per la modifica di range
Una volta creato il range, possiamo manipolare il contenuto con i seguenti metodi:
deleteContents()
– rimuove il contenuto del range dal documentoextractContents()
– rimuove il contenuto del range dal documento e lo ritorna come DocumentFragmentcloneContents()
– clona un contenuto del range e lo restituisce come DocumentFragmentinsertNode(node)
– inseriscenode
nel documento all’inizio del rangesurroundContents(node)
– avvolgenode
attorno al contenuto range. Per questa azione, il range deve contenere i tag di apertura e chiusura per tutti gli elementi dentro di esso: non possono esserci range del tipo<i>abc
.
Fondamentalmente, con questi metodi possiamo fare qualunque cosa con i nodi selezionati.
Ecco il banco di prova per vederli in azione:
Clicca sui pulsanti per eseguire i metodi nella selezione, "resetExample" per resettare.
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<p id="result"></p>
<script>
let range = new Range();
// Ogni metodo mostrato è rappresentato qui:
let methods = {
deleteContents() {
range.deleteContents()
},
extractContents() {
let content = range.extractContents();
result.innerHTML = "";
result.append("extracted: ", content);
},
cloneContents() {
let content = range.cloneContents();
result.innerHTML = "";
result.append("cloned: ", content);
},
insertNode() {
let newNode = document.createElement('u');
newNode.innerHTML = "NEW NODE";
range.insertNode(newNode);
},
surroundContents() {
let newNode = document.createElement('u');
try {
range.surroundContents(newNode);
} catch(e) { console.log(e) }
},
resetExample() {
p.innerHTML = `Example: <i>italic</i> and <b>bold</b>`;
result.innerHTML = "";
range.setStart(p.firstChild, 2);
range.setEnd(p.querySelector('b').firstChild, 3);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
};
for(let method in methods) {
document.write(`<div><button onclick="methods.${method}()">${method}</button></div>`);
}
methods.resetExample();
</script>
Ci sono anche metodi per confrontare i range, ma vengono usati raramente. Nel caso in cui ne avessi bisogno puoi fare riferimento alle specifiche o sul manuale MDN.
Selection
Range
è un oggetto generico per la gestione di selezioni. Comunque, creare un Range
non significa che vedremo la selezione sullo schermo.
Possiamo creare oggetti Range
, passarli in giro, ma visivamente non avranno alcun effetto.
La selezione del documento è rappresentata da un oggetto Selection
, che si può ottenere come window.getSelection()
o tramite document.getSelection()
. Una selezione può includere zero o più range. Almeno così dice la Specifica della API Selection.
In pratica, tuttavia, solamente Firefox permette di selezionare range multipli nel documento, attraverso la combinazione di tasti Ctrl+click (Cmd+click su Mac).
Qui potete vedere uno screenshot di una selezione con 3 range, fatta su Firefox:
Gli altri browser supportano al massimo 1 range. Come vedremo, alcuni dei metodi di Selection
implicano la possibilità di avere più range, ma di nuovo, tutti i browser eccetto Firefox, ne possono avere massimo 1.
Questo è un piccolo esempio che mostra la selezione corrente come testo. Seleziona qualcosa e clicca:
Proprietà di Selection
Come detto, una selection può in teoria contenere più ranges. Possiamo ottenere questi renge usando il seguente metodo:
getRangeAt(i)
– recupera il range numeroi
, partendo da0
. In tutti i browser, ad eccezione di Firefox, viene usato solo il numero0
.
Inoltre, esistono proprietà che spesso sono più comode da usare.
In modo simile a un range, una selezione ha un inizio, chiamato “anchor”, e una fine, chiamata “focus”.
Le proprietà principali di selection sono:
anchorNode
– il nodo dove comincia la selezione,anchorOffset
– l’offset inanchorNode
dove comincia la selezione,focusNode
– il nodo in cui termina la selezione,focusOffset
– l’offset difocusNode
in cui termina la selezione,isCollapsed
–true
se la selezione non seleziona nulla (range vuoto), o non esiste.rangeCount
– conto del numero di selezioni, massimo1
su tutti i browser, eccetto Firefox.
Esiste un’importante differenza tra una selection anchor/focus e un Range
start/end.
Come sappiamo, l’ oggetto Range
ha il suo inizio prima della fine.
Per le selections, questo non è sempre vero.
le selezione tramite il mouse, può avvenire in entrambe le direzioni: sia da sinistra verso destra, che da destra verso sinistra.
In altre parole, se mentre il pulsante del mouse è premuto ci muoviamo avanti nel documento, la fine (focus) sarà dopo l’inizio (anchor)
Ad esempio, se l’utente comincia la selezione con il mouse andando da “Example” a “italic”:
Ma la stessa selezione potrebbe essere effettuata anche a ritroso, partendo da “italic” a “Example” (direzione contraria), in questo caso la fine (focus) sarà prima dell’inizio (anchor):
Eventi di Selection
Ci sono eventi per tenere traccia della selezione:
elem.onselectstart
– quando una selezione inizia su specificatamente suelem
(o al suo interno). Ad esempio, quando l’utente preme il pulsante del mouse sull’elemento e poi muove il puntatore.- Prevenire l’azione predefinita (prevent default), fa in modo che la selezione non cominci. Quindi iniziare una selezione dall’elemento diventa impossibile, ma l’elemento è ancora selezionabile. Il visitatore deve solo iniziare la selezione da altrove.
document.onselectionchange
– ogni volta che una selezione viene modificata.- Nota bene: questo gestore può essere impostato solo su
document
.
- Nota bene: questo gestore può essere impostato solo su
Demo di tracciamento per Selection
Ecco una piccola demo che tiene traccia della selezione corrente sul documento
e mostra i suoi confini:
<p id="p">Select me: <i>italic</i> and <b>bold</b></p>
From <input id="from" disabled> – To <input id="to" disabled>
<script>
document.onselectionchange = function() {
let selection = document.getSelection();
let {anchorNode, anchorOffset, focusNode, focusOffset} = selection;
// anchorNode and focusNode are text nodes usually
from.value = `${anchorNode?.data}, offset ${anchorOffset}`;
to.value = `${focusNode?.data}, offset ${focusOffset}`;
};
</script>
Copiare una selezione
Ci sono due approcci alla copia del contenuto di una selezione:
- Possiamo usare
document.getSelection().toString()
per avere il contenuto come testo. - Oppure possiamo copiare il DOM completo (ad esempio se volgiamo mantenere la formattazione) recuperando i ranges con
getRangesAt(...)
. Un oggettoRange
possiede il metodocloneContents()
che clona il contenuto e lo ritorna come oggettoDocumentFragment
, che può essere inserito dove necessario.
Ecco la demo per copiare il contenuto selezionato, sia come testo che come nodi del DOM:
<p id="p">Select me: <i>italic</i> and <b>bold</b></p>
Cloned: <span id="cloned"></span>
<br>
As text: <span id="astext"></span>
<script>
document.onselectionchange = function() {
let selection = document.getSelection();
cloned.innerHTML = astext.innerHTML = "";
// Clona i nodi a partire dal range (qui viene supportata la multiselezione)
for (let i = 0; i < selection.rangeCount; i++) {
cloned.append(selection.getRangeAt(i).cloneContents());
}
// Ottiene come testo
astext.innerHTML += selection;
};
</script>
Metodi per la selezione
Possiamo lavorare con la selezione aggiungendo/rimuovendo ranges:
getRangeAt(i)
– legge il range alla posizione i, partendo da ‘0’. In tutti i browser, tranne Firefox, viene usato solo0
.addRange(range)
– aggiungerange
alla selezione. Tutti i browser, eccetto Firefox, ignorano la chiamata, se la selezione ha già un range associato.removeRange(range)
– Rimuoverange
dalla selezione.removeAllRanges()
– Rimuove tutti i range.empty()
– alias perremoveAllRanges
.
Inoltre, ci sono metodi di utilità per manipolare direttamente il range di selezione, senza Range
:
collapse(node, offset)
– rimpiazza il range selezionato con un nuovo range che comincia danode
, alla posizioneoffset
.setPosition(node, offset)
– alias dicollapse
.collapseToStart()
– collassa (sostituisce con un nodo vuoto) all’inizio della selezione,collapseToEnd()
– collassa alla fine della selezione,extend(node, offset)
– muove il focus della selezione al nodonode
, alla posizioneoffset
,setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset)
– sostituisce il range selezionato con quello che ha inizio inanchorNode/anchorOffset
e fine infocusNode/focusOffset
. Tutto il contenuto in mezzo viene selezionato.selectAllChildren(node)
– seleziona tutti i figli dinode
.deleteFromDocument()
– rimuove il contenuto selezionato dal documento.containsNode(node, allowPartialContainment = false)
– controlla se la selezione contienenode
(o una sua porzione, se il secondo argomento ètrue
)
Nella maggior parte dei casi, quindi, possiamo semplicemente utilizzare i metodi offerti da Selection
, senza dover accedere agli oggetti Range
sottostanti.
Per esempio, per selezionare l’intero contenuto del paragrafo <p>
:
<p id="p">Select me: <i>italic</i> and <b>bold</b></p>
<script>
// seleziona dal figlio #0 di <p> all'ultimo figlio
document.getSelection().setBaseAndExtent(p, 0, p, p.childNodes.length);
</script>
Stessa cosa, utilizzando però i range:
<p id="p">Select me: <i>italic</i> and <b>bold</b></p>
<script>
let range = new Range();
range.selectNodeContents(p); // oppure selectNode(p) per selezionare il tag <p>
document.getSelection().removeAllRanges(); // pulisce la selezione se esiste
document.getSelection().addRange(range);
</script>
Nel caso in cui ci fosse già una selezione attiva, va prima rimossa tramite removeAllRanges()
. Una volta eliminata, sarà possibile aggiungere un nuovo Range
. Diversamente, tutti i browser eccetto Firefox, ignoreranno i nuovi range.
L’eccezione a questa regola sono i metodi di selezione, che sostituiscono la selezione esistente, come setBaseAndExtent
.
Selezione nei controlli dei form
Gli elementi dei form, come input
e textarea
forniscono API speciali per la selezione, senza l’ausilio degli oggetti Selection
o Range
. Dato che un valore di input è testo puro, e non HTML, non è necessario l’utilizzo di questi due oggetti, poiché è tutto più semplificato.
Proprietà:
input.selectionStart
– posizione dell’inizio della selezione (scrivibile),input.selectionEnd
– posizione della fine della selezione (scrivibile),input.selectionDirection
– direzione della selezione, un valore tra: “forward”, “backward” o “none” (ad esempio, la selezione attraverso il doppio click del mouse),
Eventi:
input.onselect
– viene innescato alla selezione di un elemento.
Metodi:
-
input.select()
– seleziona l’intero contenuto dell’area di testo (può essere unatextarea
invece cheinput
), -
input.setSelectionRange(start, end, [direction])
– modifica la selezione, selezionando il contenuto compreso trastart
fino aend
, in una data direzione (opzionale). -
input.setRangeText(replacement, [start], [end], [selectionMode])
– sostituisce un range di testo con nuovo testo.Se forniti, gli argomenti opzionali
start
edend
, impostano l’inizio e la fine del range, altrimenti viene usata la selezione dell’utente.L’ultimo argomento,
selectionMode
, determina come la selezione verrà impostata dopo che il testo verrà rimpiazzato. I valori possibili sono:"select"
– il nuovo testo inserito, verrà selezionato."start"
– il range di selezione collasserà subito prima del testo inserito (il cursore verrà posizionato subito prima di esso)."end"
– il range di selezione collassa subito dopo del testo inserito (il cursore verrà posizionato alla sua destra)."preserve"
– tenta di preservare la selezione. Questo è il comportamento predefinito.
Vediamo questi metodi in azione.
Esempio: tenere traccia della selezione
Questo codice usa l’evento onselect
per tenere traccia della selezione:
<textarea id="area" style="width:80%;height:60px">
Selecting in this text updates values below.
</textarea>
<br>
From <input id="from" disabled> – To <input id="to" disabled>
<script>
area.onselect = function() {
from.value = area.selectionStart;
to.value = area.selectionEnd;
};
</script>
Nota bene:
onselect
viene innescato quando viene selezionato un elemento, ma non quando la selezione viene rimossa.- l’evento
document.onselectionchange
non dovrebbe essere innescato per selezioni dentro un controllo di un form, secondo le specifiche, dal momento che non è correlato alla selezione e range deldocument
. Alcuni browser lo emettono in ogni caso, ma non possiamo farci affidamento.
Esempio: spostare il cursore
Possiamo modificare selectionStart
e selectionEnd
, che impostano la selezione.
Un importante caso limite è quando selectionStart
and selectionEnd
si equivalgono. Ciò rappresenta esattamente la posizione del cursore. Oppure, riformulando, quando non c’è nulla di selezionato, la selezione è collassata nella posizione del cursore.
In questo modo, impostando selectionStart
e selectionEnd
allo stesso valore, spostiamo il cursore.
Ad esempio:
<textarea id="area" style="width:80%;height:60px">
Focus on me, the cursor will be at position 10.
</textarea>
<script>
area.onfocus = () => {
// setTimeout impostato a zero, per eseguirlo subito dopo il che il "focus" viene completato.
setTimeout(() => {
// Possiamo impostare qualunque selezione
// se start=end, il cursore è esattamente in quel punto
area.selectionStart = area.selectionEnd = 10;
});
};
</script>
Esempio: modifica della selezione
Per modificare il contenuto della selezione, possiamo usare il metodo input.setRangeText()
. Di sicuro, possiamo leggere selectionStart/End
e, conoscendo la selezione, possiamo cambiare la corrispondente sottostringa di value
, ma setRangeText
è molto più potente e spesso più conveniente.
Questo è un metodo in qualche maniera complesso. Nella sua forma più semplice con un solo argomento, sostituisce il range selezionato dall’utente e rimuove la selezione.
For example, here the user selection will be wrapped by *...*
:
<input id="input" style="width:200px" value="Select here and click the button">
<button id="button">Avvolge la selezione tra asterischi *...*</button>
<script>
button.onclick = () => {
if (input.selectionStart == input.selectionEnd) {
return; // nessuna selezione
}
let selected = input.value.slice(input.selectionStart, input.selectionEnd);
input.setRangeText(`*${selected}*`);
};
</script>
With more arguments, we can set range start
and end
.
In this example we find "THIS"
in the input text, replace it and keep the replacement selected:
<input id="input" style="width:200px" value="Replace THIS in text">
<button id="button">Sostituisce THIS</button>
<script>
button.onclick = () => {
let pos = input.value.indexOf("THIS");
if (pos >= 0) {
input.setRangeText("*THIS*", pos, pos + 4, "select");
input.focus(); // focus per mantenere la selezione visibile
}
};
</script>
Esempio: inserimento nella posizione del cursore
Se non c’è nulla di selezionato, o se start
ed end
si equivalgono in setRangeText
, allora il nuovo testo verrà solo inserito e non verrà rimosso nulla.
Possiamo anche inserire qualcosa “nella posizione del cursore” usando setRangeText
.
Qui abbiamo un pulsante che inserisce "HELLO"
sul cursore, posizionandolo immediatamente dopo. Se la selezione non è vuota, allora verrà sostituita (possiamo riconoscerla confrontando selectionStart!=selectionEnd
o facendo qualcos’altro):
<input id="input" style="width:200px" value="Text Text Text Text Text">
<button id="button">Inserisci "HELLO" al cursore</button>
<script>
button.onclick = () => {
input.setRangeText("HELLO", input.selectionStart, input.selectionEnd, "end");
input.focus();
};
</script>
Impedire la selezione
Per rendere qualcosa non selezionabile, ci sono tre modi:
-
Utilizzare la proprietà CSS
user-select: none
.<style> #elem { user-select: none; } </style> <div>Selezionabile <div id="elem">Non selezionabile</div> Selezionabile</div>
Questo non permette alla selezione di cominciare su
elem
. Tuttavia l’utente può cominciare la selezione ovunque, ed includereelem
al suo interno.Quindi
elem
diverrà parte didocument.getSelection()
, e così la selezione c’è, ma il suo contenuto viene generalmente ignorato nel copia-incolla. -
Prevenire l’azione predefinita sugli eventi
onselectstart
omousedown
.<div>Selezionabile <div id="elem">Non selezionabile</div> Selezionabile</div> <script> elem.onselectstart = () => false; </script>
Questo impedisce la selezione su
elem
, ma il visitatore può cominciare la selezione su un altro elemento e successivamente estendere la selezione suelem
.Questo è comodo quando c’è un altro gestore di eventi nella stessa azione, che innesca la selezione (ad esempio
mousedown
). Facendo ciò, disabilitiamo la selezione evitando conflitti, lasciando che i contenutielem
possano ancora essere copiati. -
Possiamo anche pulire la selezione successivamente dopo che sia avvenuta tramite
document.getSelection().empty()
. Questa cosa è usata raramente, dato che causa intermittenze non volute sulla selezione che compare-scompare.
Riferimenti
- Specifiche DOM: Range
- API Selection
- Specifiche HTML: APIs per il controllo delle selezioni sul testo
Riepilogo
Abbiamo affrontato due differenti API per le selezioni:
- Per i documenti: oggetti
Selection
eRange
. - Per gli
input
,textarea
: proprietà e metodi aggiuntivi.
La seconda API è molto semplice, dato che lavora con i testi.
I codici pronti più usati sono probabilmente:
- Ottenere la selezione:
let selection = document.getSelection(); let cloned = /* elemento nel quale clonare i nodi selezionati */; // quindi applica i metodi Range su selection.getRangeAt(0) // oppure a tutti i range per supportare la multiselezione, come in questo caso for (let i = 0; i < selection.rangeCount; i++) { cloned.append(selection.getRangeAt(i).cloneContents()); }
- Impostare la selezione:
let selection = document.getSelection(); // direttamente: selection.setBaseAndExtent(...from...to...); // oppure possiamo creare una range e: selection.removeAllRanges(); selection.addRange(range);
Infine, relativamente al cursore. La posizione del cursore negli elementi editabili, come <textarea>
è sempre all’inizio o alla fine della selezione. Possiamo usarla per ottenere la posizione corrente del cursore o per muovere il cursore impostando elem.selectionStart
e elem.selectionEnd
.
Commenti
<code>
, per molte righe – includile nel tag<pre>
, per più di 10 righe – utilizza una sandbox (plnkr, jsbin, codepen…)