W3docs

API Selection e Range in JavaScript

Scopri come usare le API Selection e Range di JavaScript per leggere, modificare e manipolare il testo selezionato nel DOM.

Quando un utente trascina il cursore sul testo di una pagina, il browser tiene traccia di ciò che è stato selezionato. JavaScript espone queste informazioni — e consente di creare selezioni a livello di codice — attraverso due interfacce DOM correlate:

  • Un Range è una coppia di punti limite (un inizio e una fine) all'interno del documento. Descrive quale parte del documento si intende, fino a uno specifico offset di carattere all'interno di un nodo di testo. Un range può esistere puramente in memoria senza che l'utente ne sia consapevole.
  • Una Selection è ciò che l'utente ha attualmente evidenziato. È essenzialmente un wrapper attorno a uno o più range, legato all'evidenziazione sullo schermo e al caret (cursore di testo).

Si ricorre a queste API quando si vuole creare un editor di testo ricco, una funzionalità di "evidenzia e commenta", una ricerca e sostituzione personalizzata, o qualsiasi cosa che legga, sposti o applichi stili al testo selezionato a livello di codice. Questo capitolo tratta la costruzione dei range, la lettura e la modifica della selezione dell'utente, e come combinarle per evidenziare e inserire contenuto.

Questo argomento si basa sulla struttura del DOM. Se i tipi di nodo e gli offset sono nuovi per te, leggi prima Proprietà del nodo: tipo, tag e contenuto e Modifica del documento.

Comprendere l'interfaccia Selection

L'interfaccia Selection rappresenta il testo evidenziato dall'utente, oppure la posizione corrente del caret quando non è evidenziato nulla. Vi si accede tramite il metodo globale window.getSelection() (spesso scritto semplicemente come getSelection()). Internamente una selezione contiene zero o più oggetti Range; in pratica la maggior parte dei browser mantiene un solo range, quindi getRangeAt(0) è il modo comune per leggerlo.

Il modo più rapido per vedere cosa è selezionato è toString(), che restituisce il testo selezionato come stringa semplice:

// After the user highlights something on the page:
const selectedText = window.getSelection().toString();
console.log(selectedText); // whatever the user highlighted

Proprietà e metodi utili di Selection

  • rangeCount: Il numero di range nella selezione — 0 quando non è selezionato nulla. Verificare sempre questo valore prima di chiamare getRangeAt.
  • toString(): Restituisce il testo selezionato come stringa.
  • getRangeAt(index): Restituisce il Range all'indice specificato (usare 0 per la selezione corrente).
  • addRange(range): Aggiunge un Range alla selezione, evidenziandolo sullo schermo.
  • removeAllRanges(): Cancella completamente la selezione.
  • removeRange(range): Rimuove un range specifico. La maggior parte dei browser mantiene un solo range, quindi removeAllRanges() è la scelta pratica.
  • collapse(node, offset): Riduce la selezione a un singolo punto (un caret vuoto) all'interno di node.

Uno schema comune è leggere la selezione corrente, modificarla, quindi riscrivere una nuova selezione — removeAllRanges() seguito da addRange().

Esempio pratico: evidenziare il testo

Per evidenziare ciò che l'utente ha selezionato, si prende il suo range, si estraggono i nodi selezionati dal documento, li si inserisce in un <span> con stile e si rimette tale span dove si trovava il contenuto.

extractContents() rimuove il contenuto selezionato dal DOM e lo restituisce come frammento di documento, lasciando il range vuoto (collapsed) in quel punto — che è esattamente dove inseriamo poi lo span.

<div id="text">Select some of this text and press the button.</div>
<button onclick="highlightText()">Highlight</button>

<script>
function highlightText() {
  const selection = window.getSelection();
  if (!selection.rangeCount) return false;
  const range = selection.getRangeAt(0);
  const span = document.createElement('span');
  span.style.backgroundColor = 'yellow';
  const fragment = range.extractContents();
  span.appendChild(fragment);
  range.insertNode(span);
}
</script>

Esplorare l'interfaccia Range

Un Range delimita un frammento del documento con due punti limite — un inizio e una fine — ciascuno definito da un nodo e da un offset. Il significato dell'offset dipende dal nodo:

  • All'interno di un nodo di testo, l'offset è un indice di carattere (es. offset 5 è tra il 5° e il 6° carattere).
  • All'interno di un nodo elemento, l'offset conta i nodi figlio (es. offset 0 è prima del primo figlio).

Si crea un range vuoto con document.createRange(), poi si posizionano i suoi limiti. È possibile trovare i nodi a cui puntare con getElementById / querySelector.

Impostare i limiti

const p = document.querySelector('p');
const textNode = p.firstChild;        // the text inside <p>

const range = document.createRange();
range.setStart(textNode, 0);          // start at the first character
range.setEnd(textNode, 5);            // end before the 6th character
console.log(range.toString());        // first 5 characters of the paragraph

Due scorciatoie coprono i casi più comuni così da non dover calcolare gli offset:

  • selectNode(node) — il range comprende il nodo e i suoi tag circostanti.
  • selectNodeContents(node) — il range comprende solo ciò che si trova all'interno del nodo.

Metodi utili di Range

  • setStart(node, offset) / setEnd(node, offset): Posizionano i limiti di inizio e fine.
  • selectNode(node) / selectNodeContents(node): Impostano entrambi i limiti attorno a un nodo o al suo contenuto.
  • toString(): Il testo all'interno del range.
  • cloneContents(): Restituisce una copia del contenuto del range come frammento di documento, senza toccare il documento.
  • extractContents(): Sposta il contenuto fuori dal documento in un frammento e lo restituisce.
  • deleteContents(): Rimuove il contenuto del range senza restituire nulla.
  • cloneRange(): Restituisce una copia dell'oggetto range stesso (non del suo contenuto).
  • insertNode(node): Inserisce un nodo all'inizio del range.
  • surroundContents(node): Avvolge il contenuto del range all'interno di node — utile per l'evidenziazione, ma genera un errore se il range attraversa parzialmente un elemento non testuale.

Ricordare la differenza: cloneContents() copia, extractContents() sposta fuori e restituisce, deleteContents() rimuove e scarta.

Esempio pratico: estrarre testo

Questo codice legge tutto il contenuto all'interno di un elemento con un range e trasforma il testo senza toccare il DOM originale:

<div id="content">This is some sample text for extraction.</div>
<button onclick="extractText()">Extract and Manipulate</button>

<script>
function extractText() {
  const range = document.createRange();
  const content = document.getElementById('content');
  range.selectNodeContents(content);
  const extractedText = range.toString();
  const manipulatedText = extractedText.replace('sample', 'example'); // Manipulating text
  alert(manipulatedText);
}
</script>

Nell'esempio precedente, lo script sostituisce la parola "sample" con "example" nel testo estratto prima di mostrarlo in una finestra di avviso. Si tratta di una manipolazione di base, ma dimostra come si possa iniziare a lavorare con il testo una volta estratto.

Operazioni avanzate sul testo

Oltre alla manipolazione di base del testo, le interfacce Selection e Range consentono operazioni più complesse, come l'inserimento di nodi direttamente nel documento.

Esempio: inserimento di testo

Questo esempio utilizza un div con contenteditable: l'utente fa clic per posizionare il caret e il pulsante inserisce 'Hello World' esattamente in quel punto. Si noti come deleteContents() rimuova prima il testo selezionato, poi il nuovo nodo di testo viene inserito e ri-selezionato in modo che il caret si posizioni dopo di esso.

<div id="editable" contenteditable="true" style="border: 1px solid #ccc; padding: 10px; min-height: 50px;">
  Click here and set the cursor position.
</div>
<button onclick="insertText()">Insert 'Hello World'</button>

<script>
function insertText() {
  const editableDiv = document.getElementById('editable');
  const sel = window.getSelection();
  
  // Check if the selection is within the editable div
  if (!sel.rangeCount || !editableDiv.contains(sel.getRangeAt(0).commonAncestorContainer)) return;
  
  const range = sel.getRangeAt(0);
  range.deleteContents();  // Clears any selected text

  const textNode = document.createTextNode('Hello World');
  range.insertNode(textNode);

  sel.removeAllRanges();   // Clear the previous selection
  sel.addRange(range);     // Re-select the new text node
}
</script>
  • Un Range è composto da due punti limite (nodo + offset) che descrivono un frammento del documento; si crea con document.createRange() e si posiziona con setStart/setEnd oppure con le scorciatoie selectNode/selectNodeContents.
  • Una Selection, ottenuta da window.getSelection(), racchiude l'evidenziazione dell'utente sullo schermo. Si legge con toString() e getRangeAt(0); si riscrive con removeAllRanges() + addRange().
  • Per il contenuto: cloneContents() copia, extractContents() sposta fuori, deleteContents() scarta, mentre insertNode / surroundContents reinseriscono i nodi.

Insieme, questi strumenti consentono di evidenziare, estrarre e inserire contenuto con precisione — la base degli editor di testo ricchi e degli strumenti di annotazione.

Continua: Modifica del documento · Proprietà del nodo: tipo, tag e contenuto · Ricerca: getElement* e querySelector

Pratica

Pratica
Quali delle seguenti affermazioni sono vere riguardo alle interfacce Range e Selection di JavaScript?
Quali delle seguenti affermazioni sono vere riguardo alle interfacce Range e Selection di JavaScript?
Was this page helpful?