Link in grassetto dove nessuno ha mai aggiunto link prima: frammenti di testo

I frammenti di testo ti consentono di specificare uno snippet di testo nel frammento di URL. Quando si accede a un URL con un frammento di testo di questo tipo, il browser può enfatizzarlo e/o richiamarlo all'attenzione dell'utente.

Identificatori dei frammenti

Chrome 80 è stata una release importante. Conteneva una serie di funzionalità molto attese, come i moduli ECMAScript nei web worker, l'unione di valori null, la catenabilità facoltativa e altre ancora. Come di consueto, la release è stata annunciata tramite un post del blog sul blog di Chromium. Puoi vedere un estratto del post del blog nello screenshot di seguito.

Post del blog di Chromium con caselle rosse attorno agli elementi con un attributo id.

Probabilmente ti starai chiedendo cosa significano tutte le caselle rosse. Sono il risultato dell'esecuzione del seguente snippet in DevTools. Evidenzia tutti gli elementi che hanno un attributo id.

document.querySelectorAll('[id]').forEach((el) => {
  el.style.border = 'solid 2px red';
});

Posso inserire un link diretto a qualsiasi elemento evidenziato con una casella rossa grazie all'identificatore di frammento che poi utilizzo nell'hash dell'URL della pagina. Supponiamo che io voglia creare un link diretto alla casella Inviaci un feedback nei nostri forum dei prodotti nel riquadro a parte. Posso farlo creando manualmente l'URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1. Come puoi vedere nel riquadro Elementi degli Strumenti per sviluppatori, l'elemento in questione ha un attributo id con il valore HTML1.

Strumenti per sviluppatori che mostrano il id di un elemento.

Se analizzo questo URL con il costruttore URL() di JavaScript, vengono visualizzati i diversi componenti. Nota la proprietà hash con il valore #HTML1.

new URL('https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1');
/* Creates a new `URL` object
URL {
  hash: "#HTML1"
  host: "blog.chromium.org"
  hostname: "blog.chromium.org"
  href: "https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1"
  origin: "https://blog.chromium.org"
  password: ""
  pathname: "/2019/12/chrome-80-content-indexing-es-modules.html"
  port: ""
  protocol: "https:"
  search: ""
  searchParams: URLSearchParams {}
  username: ""
}
*/

Tuttavia, il fatto che abbia dovuto aprire Strumenti per sviluppatori per trovare il id di un elemento la dice lunga sulla probabilità che questa particolare sezione della pagina fosse destinata a essere collegata dall'autore del post del blog.

Cosa succede se voglio fare un link a qualcosa senza un id? Supponiamo che io voglia aggiungere un link all'intestazione Moduli ECMAScript nei worker web. Come puoi vedere nello screenshot di seguito, l'elemento <h1> in questione non ha un attributo id, il che significa che non posso collegarmi a questa intestazione. Questo è il problema che viene risolto da Text Fragments.

DevTools mostra un'intestazione senza un id.

Frammenti di testo

La proposta Frammenti di testo aggiunge il supporto per la specifica di uno snippet di testo nell'hash dell'URL. Quando si accede a un URL con un frammento di testo di questo tipo, lo user agent può sottolinearlo e/o richiamarlo all'attenzione dell'utente.

Compatibilità del browser

Supporto dei browser

  • Chrome: 89.
  • Edge: 89.
  • Firefox: 131.
  • Safari Technology Preview: supportato.

Origine

Per motivi di sicurezza, la funzionalità richiede l'apertura dei link in un contesto noopener. Pertanto, assicurati di includere rel="noopener" nel markup di ancoraggio <a> o di aggiungere noopener all'elencoWindow.open() delle funzionalità della finestra.

start

Nella sua forma più semplice, la sintassi dei frammenti di testo è la seguente: il simbolo hash # seguito da :~:text= e infine da start, che rappresenta il testo codificato in percentuale auquel voglio collegarmi.

#:~:text=start

Ad esempio, supponiamo che io voglia fare un link all'intestazione Moduli ECMAScript nei worker web nel post del blog che annuncia le funzionalità di Chrome 80. In questo caso, l'URL sarà:

https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules%20in%20Web%20Workers

Il frammento di testo è sottolineato come questo. Se fai clic sul link in un browser supportato come Chrome, il frammento di testo viene evidenziato e visualizzato scorrendo:

Il frammento di testo è stato visualizzato e evidenziato.

start e end

Ora, cosa succede se voglio collegarmi all'intera sezione intitolata Moduli ECMAScript nei worker web, non solo alla sua intestazione? L'applicazione della codifica percentuale all'intero testo della sezione renderebbe l'URL risultante impraticamente lungo.

Fortunatamente, esiste un modo migliore. Invece dell'intero testo, posso inquadrare il testo desiderato utilizzando la sintassi start,end. Pertanto, specifico un paio di parole con codifica percentuale all'inizio del testo desiderato e un paio di parole con codifica percentuale alla fine del testo desiderato, separati da una virgola ,.

Ecco come si presenta:

https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules%20in%20Web%20Workers,ES%20Modules%20in%20Web%20Workers..

Per start, ho ECMAScript%20Modules%20in%20Web%20Workers, poi una virgola , seguita da ES%20Modules%20in%20Web%20Workers. come end. Quando fai clic su un browser supportato come Chrome, l'intera sezione viene evidenziata e visualizzata tramite scorrimento:

Il frammento di testo è stato visualizzato e evidenziato.

Ora potresti chiederti perché ho scelto start e end. In realtà, anche l'URL leggermente più breve https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules,Web%20Workers. con solo due parole su ciascun lato avrebbe funzionato. Confronta start e end con i valori precedenti.

Se faccio un ulteriore passo avanti e ora uso una sola parola sia per start che per end, puoi vedere che ho un problema. L'URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript,Workers. ora è ancora più breve, ma il frammento di testo evidenziato non è più quello desiderato inizialmente. L'evidenziazione si interrompe alla prima occorrenza della parola Workers., che è corretta, ma non è ciò che intendevo evidenziare. Il problema è che la sezione desiderata non è identificata in modo univoco dai valori start e end attuali di una sola parola:

Frammento di testo non intenzionale visualizzato e evidenziato.

prefix- e -suffix

L'utilizzo di valori sufficientemente lunghi per start e end è una soluzione per ottenere un link univoco. Tuttavia, in alcuni casi non è possibile. A proposito, perché ho scelto il post del blog relativo al rilascio di Chrome 80 come esempio? La risposta è che in questa release sono stati introdotti i frammenti di testo:

Testo del post del blog: frammenti di URL di testo. Ora gli utenti o gli autori possono creare un link a una parte specifica di una pagina utilizzando un frammento di testo fornito in un URL. Quando la pagina viene caricata, il browser mette in evidenza il testo e fa scorrere il frammento in modo che sia visibile. Ad esempio, l&#39;URL riportato di seguito carica una pagina della wiki per &quot;Gatto&quot; e scorre fino ai contenuti elencati nel parametro &quot;text&quot;.
Estratto del post del blog relativo all'annuncio di Text Fragments.

Nello screenshot qui sopra, la parola "testo" appare quattro volte. La quarta occorrenza è scritta in un carattere di codice verde. Se volessi inserire un link a questa parola specifica, imposterei start su text. Poiché la parola "testo" è, in effetti, una sola parola, non può esserci un end. E ora? L'URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=text viene associato alla prima occorrenza della parola "Testo" già presente nell'intestazione:

Corrispondenza del frammento di testo alla prima occorrenza di "Testo".

Fortunatamente, esiste una soluzione. In casi come questo, posso specificare un prefix​- e un -suffix. La parola prima del carattere "text" del codice verde è "the" e la parola dopo è "parameter". Nessuna delle altre tre occorrenze della parola "testo" ha le stesse parole circostanti. Grazie a queste informazioni, posso modificare l'URL precedente e aggiungere prefix- e -suffix. Come per gli altri parametri, anche questi devono essere codificati in percentuale e possono contenere più di una parola. https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=the-,text,-parameter. Per consentire al parser di identificare chiaramente prefix- e -suffix, questi devono essere separati da start e dall'eventuale end con un trattino -.

Corrispondenza del frammento di testo all'occorrenza desiderata di "testo".

La sintassi completa

La sintassi completa dei frammenti di testo è riportata di seguito. Le parentesi quadre indicano un parametro facoltativo. I valori di tutti i parametri devono essere codificati in percentuale. Questo è particolarmente importante per i caratteri trattino-, e commerciale & e virgola ,, in modo che non vengano interpretati come parte della sintassi della direttiva text.

#:~:text=[prefix-,]start[,end][,-suffix]

Ognuno di prefix-, start, end e -suffix corrisponderà solo al testo all'interno di un singolo elemento a livello di blocco, ma gli intervalli start,end completi possono estendersi su più blocchi. Ad esempio, :~:text=The quick,lazy dog non corrisponderà nell'esempio seguente, perché la stringa iniziale "Il rapido" non compare all'interno di un singolo elemento a livello di blocco ininterrotto:

<div>
  The
  <div></div>
  quick brown fox
</div>
<div>jumped over the lazy dog</div>

Tuttavia, corrisponde in questo esempio:

<div>The quick brown fox</div>
<div>jumped over the lazy dog</div>

Creazione di URL di frammenti di testo con un'estensione del browser

Creare manualmente gli URL dei frammenti di testo è un'operazione tediosa, soprattutto per assicurarsi che siano unici. Se proprio vuoi, la specifica contiene alcuni suggerimenti e elenca i passaggi esatti per generare gli URL dei frammenti di testo. Forniamo un'estensione del browser open source chiamata Link to Text Fragment (Collega a frammento di testo) che ti consente di collegarti a qualsiasi testo selezionandolo e facendo clic su "Copia link al testo selezionato" nel menu contestuale. Questa estensione è disponibile per i seguenti browser:

Estensione del browser Link to Text Fragment .

Più frammenti di testo in un URL

Tieni presente che in un URL possono essere visualizzati più frammenti di testo. I singoli frammenti di testo devono essere separati da un carattere e commerciale &. Ecco un esempio di link con tre frammenti di testo: https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=Text%20URL%20Fragments&text=text,-parameter&text=:~:text=On%20islands,%20birds%20can%20contribute%20as%20much%20as%2060%25%20of%20a%20cat's%20diet.

Tre frammenti di testo in un URL.

Combinazione di elementi e frammenti di testo

I frammenti di elementi tradizionali possono essere combinati con i frammenti di testo. È perfettamente possibile avere entrambi nello stesso URL, ad esempio per fornire un'opzione di riserva significativa nel caso in cui il testo originale sulla pagina vari, in modo che il frammento di testo non corrisponda più. L'URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1:~:text=Give%20us%20feedback%20in%20our%20Product%20Forums. che rimanda alla sezione Inviaci un feedback nei nostri forum di prodotto contiene sia un frammento di elemento (HTML1) sia un frammento di testo (text=Give%20us%20feedback%20in%20our%20Product%20Forums.):

Collegamento con frammento di elemento e frammento di testo.

La direttiva del frammento

C'è un elemento della sintassi che non ho ancora spiegato: la direttiva del frammento :~:. Per evitare problemi di compatibilità con i frammenti di elementi URL esistenti, come mostrato sopra, la specifica dei frammenti di testo introduce la direttiva fragment. La direttiva del frammento è una parte del frammento di URL delimitata dalla sequenza di codice:~:. È riservato alle istruzioni dello user agent, come text=, e viene rimosso dall'URL durante il caricamento in modo che gli script dell'autore non possano interagire direttamente con esso. Le istruzioni dell'user agent vengono anche chiamate istruzioni. Nel caso specifico, text= è quindi chiamata direttiva di testo.

Rilevamento di funzionalità

Per rilevare il supporto, verifica la presenza della proprietà fragmentDirective di sola lettura in document. La direttiva frammento è un meccanismo per gli URL per specificare istruzioni rivolte al browser anziché al documento. Ha lo scopo di evitare l'interazione diretta con lo script dell'autore, in modo che le future istruzioni dell'agente utente possano essere aggiunte senza il timore di introdurre modifiche che comportino l'interruzione dei contenuti esistenti. Un potenziale esempio di queste aggiunte future potrebbe essere i suggerimenti di traduzione.

if ('fragmentDirective' in document) {
  // Text Fragments is supported.
}

Il rilevamento delle funzionalità è pensato principalmente per i casi in cui i link vengono generati dinamicamente (ad esempio dai motori di ricerca) per evitare di pubblicare link a frammenti di testo per i browser che non li supportano.

Applicazione di stili ai frammenti di testo

Per impostazione predefinita, i browser applicano gli stili ai frammenti di testo nello stesso modo in cui li applicano mark (in genere nero su giallo, i colori di sistema CSS per mark). Il foglio di stile dello user-agent contiene CSS simile al seguente:

:root::target-text {
  color: MarkText;
  background: Mark;
}

Come puoi vedere, il browser espone un pseudo selettore ::target-text che puoi utilizzare per personalizzare l'evidenziazione applicata. Ad esempio, puoi progettare i frammenti di testo in modo che siano di colore nero su sfondo rosso. Come sempre, assicurati di controllare il contrasto dei colori in modo che lo stile sostitutivo non causi problemi di accessibilità e assicurati che l'evidenziazione risalti visivamente rispetto al resto dei contenuti.

:root::target-text {
  color: black;
  background-color: red;
}

Polyfillability

La funzionalità Frammenti di testo può essere sottoposta a polyfill in una certa misura. Forniamo un polyfill, utilizzato internamente dall'estensione, per i browser che non forniscono il supporto integrato per i frammenti di testo in cui la funzionalità è implementata in JavaScript.

Il polyfill contiene un filefragment-generation-utils.js che puoi importare e utilizzare per generare link a frammenti di testo. Questo è illustrato nell'esempio di codice riportato di seguito:

const { generateFragment } = await import('https://unpkg.com/text-fragments-polyfill/dist/fragment-generation-utils.js');
const result = generateFragment(window.getSelection());
if (result.status === 0) {
  let url = `${location.origin}${location.pathname}${location.search}`;
  const fragment = result.fragment;
  const prefix = fragment.prefix ?
    `${encodeURIComponent(fragment.prefix)}-,` :
    '';
  const suffix = fragment.suffix ?
    `,-${encodeURIComponent(fragment.suffix)}` :
    '';
  const start = encodeURIComponent(fragment.textStart);
  const end = fragment.textEnd ?
    `,${encodeURIComponent(fragment.textEnd)}` :
    '';
  url += `#:~:text=${prefix}${start}${end}${suffix}`;
  console.log(url);
}

Ottenere frammenti di testo a scopo di analisi

Molti siti utilizzano il frammento per il routing, motivo per cui i browser rimuovono i frammenti di testo per non interrompere queste pagine. Esiste un bisogno riconosciuto di esporre i link ai frammenti di testo alle pagine, ad esempio per scopi di analisi, ma la soluzione proposta non è ancora implementata. Come soluzione alternativa, per il momento puoi utilizzare il codice riportato di seguito per estrarre le informazioni che ti interessano.

new URL(performance.getEntries().find(({ type }) => type === 'navigate').name).hash;

Sicurezza

Le direttive dei frammenti di testo vengono richiamate solo per le navigazioni complete (non nella stessa pagina) che sono il risultato di un'attivazione dell'utente. Inoltre, le navigazioni provenienti da un'origine diversa da quella di destinazione richiedono che la navigazione venga eseguita in un contesto noopener, in modo che la pagina di destinazione sia nota per essere sufficientemente isolata. Le direttive dei frammenti di testo vengono applicate solo al frame principale. Ciò significa che il testo non verrà cercato all'interno degli iframe e la navigazione negli iframe non attiverà un frammento di testo.

Privacy

È importante che le implementazioni della specifica dei frammenti di testo non lascino intendere se un frammento di testo è stato trovato o meno in una pagina. I frammenti di elementi sono completamente sotto il controllo dell'autore della pagina originale, mentre i frammenti di testo possono essere creati da chiunque. Ricordi che nel mio esempio precedente non c'era modo di fare un link all'intestazione ECMAScript Modules in Web Workers, poiché <h1> non aveva un id, ma chiunque, incluso me, poteva semplicemente fare un link a qualsiasi pagina creando con cura il frammento di testo?

Immagina che io gestisca una rete pubblicitaria malvagia evil-ads.example.com. Immagina inoltre che in uno dei miei iframe annunci abbia creato dinamicamente un iframe cross-origin nascosto per dating.example.com con un URL Text Fragment dating.example.com#:~:text=Log%20Out non appena l'utente interagisce con l'annuncio. Se viene trovato il testo "Uscire", so che la vittima ha eseguito l'accesso a dating.example.com, che potrei utilizzare per il profilo dell'utente. Poiché un'implementazione ingenua di TextFragments potrebbe decidere che una corrispondenza riuscita debba causare un trasferimento dell'attenzione, su evil-ads.example.com potrei ascoltare l'evento blur e quindi sapere quando si è verificata una corrispondenza. In Chrome abbiamo implementato i frammenti di testo in modo che lo scenario precedente non possa verificarsi.

Un altro attacco potrebbe consistere nell'exploitare il traffico di rete in base alla posizione dello scorrimento. Supponiamo che io abbia accesso ai log del traffico di rete della mia vittima, ad esempio come amministratore dell'intranet di un'azienda. Ora immagina che esista un lungo documento delle risorse umane Che cosa fare se soffri di… e poi un elenco di condizioni come burn out, ansia e così via. Potrei inserire un pixel di monitoraggio accanto a ogni voce dell'elenco. Se poi stabilisco che il caricamento del documento avviene contemporaneamente al caricamento del pixel di monitoraggio accanto, ad esempio, all'elemento burn out, posso stabilire, in qualità di amministratore dell'intranet, che un dipendente ha fatto clic su un link a un frammento di testo con :~:text=burn%20out che il dipendente potrebbe aver assunto essere riservato e non visibile a nessuno. Poiché questo esempio è in qualche modo artificioso e poiché il suo sfruttamento richiede il rispetto di prerequisiti molto specifici, il team di sicurezza di Chrome ha valutato il rischio di implementare lo scorrimento durante la navigazione come gestibile. Altri user agent potrebbero decidere di mostrare un elemento dell'interfaccia utente di scorrimento manuale.

Per i siti che non vogliono partecipare, Chromium supporta un valore dell'intestazione Document Policy che possono inviare in modo che gli user agent non elaborino gli URL dei frammenti di testo.

Document-Policy: force-load-at-top

Disattivare i frammenti di testo

Il modo più semplice per disattivare la funzionalità è utilizzare un'estensione che può iniettare intestazioni di risposta HTTP, ad esempio ModHeader (non un prodotto Google), per inserire un'intestazione di risposta (non richiesta) come segue:

Document-Policy: force-load-at-top

Un altro modo, più complesso, per disattivare questa funzionalità è utilizzare l'impostazione Enterprise ScrollToTextFragmentEnabled. Per farlo su macOS, incolla il comando riportato di seguito nel terminale.

defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false

Su Windows, segui la documentazione sul sito di assistenza della Guida di Google Chrome Enterprise.

Per alcune ricerche, il motore di ricerca Google fornisce una risposta rapida o un riepilogo con uno snippet di contenuti di un sito web pertinente. È più probabile che questi snippet in primo piano vengano mostrati quando una ricerca viene formulata sotto forma di domanda. Se fa clic su uno snippet in primo piano, l'utente viene indirizzato direttamente al testo dello snippet in primo piano nella pagina web di origine. Questo funziona grazie agli URL dei frammenti di testo creati automaticamente.

Pagina dei risultati del motore di ricerca Google che mostra uno snippet in primo piano. La barra di stato mostra l'URL dei frammenti di testo.
Dopo aver fatto clic, la sezione pertinente della pagina viene visualizzata tramite scorrimento.

Conclusione

L'URL dei frammenti di testo è una funzionalità potente per creare link a testo arbitrario nelle pagine web. La comunità accademica può utilizzarlo per fornire citazioni o link di riferimento estremamente precisi. I motori di ricerca possono utilizzarlo per creare link diretti ai risultati di testo nelle pagine. I siti di social network possono utilizzarlo per consentire agli utenti di condividere passaggi specifici di una pagina web anziché screenshot non accessibili. Spero che tu inizi a utilizzare gli URL dei frammenti di testo e che li trovi utili come me. Assicurati di installare l'estensione del browser Link to Text Fragment.

Ringraziamenti

Text Fragments è stato implementato e specificato da Nick Burris e David Bokan, con il contributo di Grant Wang. Grazie a Joe Medley per la revisione approfondita di questo articolo. Immagine hero di Greg Rakozy su Unsplash.