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>comenodogenitore, e0come offset. Quindi possiamo impostarlo conrange.setStart(p, 0). -
La fine contiene anch’essa
<p>comenodogenitore, ma2come 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à: