Link em negrito onde ninguém havia vinculado antes: fragmentos de texto

Com os fragmentos de texto, é possível especificar um snippet de texto no fragmento do URL. Ao navegar para um URL com esse fragmento de texto, o navegador pode enfatizar e chamar a atenção do usuário para ele.

Identificadores de fragmento

O Chrome 80 foi uma grande versão. Ele continha vários recursos muito aguardados, como módulos ECMAScript em workers da Web, coalescência nula, encadeamento opcional e muito mais. Como de costume, o lançamento foi anunciado em uma postagem no blog do Chromium. Confira um trecho da postagem do blog na captura de tela a seguir.

Postagem do blog do Chromium com caixas vermelhas ao redor de elementos com um atributo id.

Você deve estar se perguntando o que significam todas as caixas vermelhas. Eles são o resultado da execução do snippet a seguir no DevTools. Ele destaca todos os elementos que têm um atributo id.

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

Posso colocar um link direto para qualquer elemento destacado com uma caixa vermelha graças ao identificador de fragmento, que uso no hash do URL da página. Se eu quisesse criar um link direto para a caixa Envie feedback nos nossos fóruns de produtos na barra lateral, poderia fazer isso criando manualmente o URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1. Como você pode ver no painel "Elementos" das Ferramentas para Desenvolvedores, o elemento em questão tem um atributo id com o valor HTML1.

Ferramentas para desenvolvedores mostrando o id de um elemento.

Se eu analisar esse URL com o construtor URL() do JavaScript, os diferentes componentes serão revelados. Observe a propriedade hash com o valor #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: ""
}
*/

No entanto, o fato de eu ter que abrir as ferramentas para desenvolvedores para encontrar o id de um elemento diz muito sobre a probabilidade de que essa seção específica da página tenha sido vinculada pelo autor da postagem do blog.

E se eu quiser vincular a algo sem um id? Digamos que eu queira criar um link para o título Módulos ECMAScript em Web Workers. Como você pode ver na captura de tela a seguir, o <h1> em questão não tem um atributo id, o que significa que não é possível vincular a esse título. Esse é o problema que os fragmentos de texto resolvem.

Ferramentas para desenvolvedores mostrando um título sem um id.

Fragmentos de texto

A proposta de fragmentos de texto adiciona suporte para especificar um snippet de texto no hash do URL. Ao navegar para um URL com esse fragmento de texto, o user agent pode enfatizá-lo e chamar a atenção do usuário.

Compatibilidade com navegadores

Browser Support

  • Chrome: 89.
  • Edge: 89.
  • Firefox: 131.
  • Safari: 18.2.

Source

Por motivos de segurança, o recurso exige que os links sejam abertos em um contexto noopener. Portanto, inclua rel="noopener" na marcação de âncora <a> ou adicione noopener à lista Window.open() de recursos de funcionalidade de janela.

start

Na forma mais simples, a sintaxe dos fragmentos de texto é a seguinte: o símbolo de hash # seguido por :~:text= e, por fim, start, que representa o texto codificado por porcentagem ao qual quero vincular.

#:~:text=start

Por exemplo, digamos que eu queira vincular ao título Módulos ECMAScript em Web Workers na postagem do blog que anuncia os recursos do Chrome 80. Nesse caso, o URL seria:

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

O fragmento de texto é destacado assim. Se você clicar no link em um navegador compatível, como o Chrome, o fragmento de texto será destacado e rolado para visualização:

Fragmento de texto rolado para a visualização e destacado.

start e end

E se eu quiser vincular a seção inteira intitulada Módulos ECMAScript em Web Workers, não apenas o título? A codificação por porcentagem de todo o texto da seção tornaria o URL resultante longo demais.

Felizmente, há uma maneira melhor. Em vez de todo o texto, posso enquadrar uma parte específica dele com a sintaxe start,end. Especifico algumas palavras codificadas por porcentagem no início do fragmento de texto e algumas palavras codificadas por porcentagem no final do fragmento de texto, separadas por uma vírgula.

Ele é assim:

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

Para start, tenho ECMAScript%20Modules%20in%20Web%20Workers, depois uma vírgula, seguida por ES%20Modules%20in%20Web%20Workers. como end. Quando você clica em um navegador compatível, como o Chrome, toda a seção é destacada e rolada para visualização:

Fragmento de texto rolado para a visualização e destacado.

Agora você pode se perguntar sobre minha escolha de start e end. Na verdade, o URL um pouco mais curto https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules,Web%20Workers. com apenas duas palavras de cada lado também teria funcionado. Compare start e end com os valores anteriores.

Se eu der mais um passo e usar apenas uma palavra para start e end, vou ter problemas. O URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript,Workers. está ainda mais curto agora, mas o fragmento de texto destacado não é mais o que foi escolhido originalmente. O destaque para na primeira ocorrência da palavra Workers., o que está correto, mas não é o que eu pretendia destacar. O problema é que a seção escolhida não é identificada de forma exclusiva pelos valores atuais de uma palavra de start e end:

Fragmento de texto não intencional rolado para visualização e destacado.

prefix- e -suffix

Usar valores longos o suficiente para start e end é uma solução para conseguir um link exclusivo. No entanto, em algumas situações, isso não é possível. Por que escolhi a postagem do blog sobre o lançamento do Chrome 80 como exemplo? A resposta é que, nesta versão, os fragmentos de texto foram introduzidos:

Texto da postagem do blog: fragmentos do URL. Agora, os usuários ou autores podem vincular a uma parte específica de uma página usando um fragmento de texto fornecido em um URL. Quando a página é carregada, o navegador destaca o texto e rola o fragmento para que ele fique visível. Por exemplo, o URL abaixo carrega uma página wiki para &quot;Cat&quot; e rola até o conteúdo listado no parâmetro `text`.
Trecho da postagem do blog sobre o anúncio de fragmentos de texto.

Observe como a palavra "text" aparece quatro vezes na captura de tela acima. A quarta ocorrência está escrita em uma fonte de código verde. Se eu quisesse vincular a essa palavra específica, definiria start como text. Como a palavra "text" é, bem, apenas uma palavra, não pode haver um end. E agora? O URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=text corresponde à primeira ocorrência da palavra "Text" já no título:

Fragmento de texto correspondente à primeira ocorrência de "Text".

Felizmente, existe uma solução. Nesses casos, posso especificar um prefix- e um -suffix. A palavra antes de "text" em fonte de código verde é "the", e a palavra depois é "parameter". Nenhuma das outras três ocorrências da palavra "texto" tem as mesmas palavras ao redor. Com esse conhecimento, posso ajustar o URL anterior e adicionar prefix- e -suffix. Assim como os outros parâmetros, eles também precisam ser codificados por porcentagem e podem conter mais de uma palavra. https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=the-,text,-parameter. Para permitir que o analisador identifique claramente o prefix- e o -suffix, eles precisam ser separados do start e do end opcional com um traço -.

Fragmento de texto correspondente à ocorrência escolhida de "text".

A sintaxe completa

A sintaxe completa dos fragmentos de texto é mostrada abaixo. Os colchetes indicam um parâmetro opcional. Os valores de todos os parâmetros precisam ser codificados por porcentagem. Isso é especialmente importante para os caracteres de traço -, e comercial & e vírgula ,, para que não sejam interpretados como parte da sintaxe da diretiva de texto.

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

Cada um dos elementos prefix-, start, end e -suffix só pode corresponder ao texto em um único elemento no nível do bloco, mas os intervalos completos de start,end podem abranger vários blocos. Por exemplo, :~:text=The quick,lazy dog não corresponde no exemplo a seguir, porque a string inicial "The quick" não aparece em um único elemento ininterrupto no nível do bloco:

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

No entanto, ele corresponde neste exemplo:

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

Como criar URLs de fragmento de texto com uma extensão do navegador

Criar URLs de fragmentos de texto manualmente é tedioso, especialmente quando se trata de garantir que eles sejam exclusivos. Se você realmente quiser, a especificação tem algumas dicas e lista as etapas exatas para gerar URLs de fragmento de texto. Oferecemos uma extensão de navegador de código aberto chamada Link para fragmento de texto que permite vincular qualquer texto selecionando-o e clicando em "Copiar link para o texto selecionado" no menu de contexto. Essa extensão está disponível para os seguintes navegadores:

Link para o fragmento de texto extensão do navegador.

Vários fragmentos de texto em um URL

Vários fragmentos de texto podem aparecer em um URL. Os fragmentos de texto específicos precisam ser separados por um caractere de "e" comercial &. Veja um exemplo de link com três fragmentos de texto: 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.

Três fragmentos de texto em um URL.

Como misturar fragmentos de elementos e texto

Os fragmentos de elementos tradicionais podem ser combinados com fragmentos de texto. Não há problema em ter os dois no mesmo URL, por exemplo, para fornecer um substituto significativo caso o texto original na página mude, de modo que o fragmento de texto não corresponda mais. O URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1:~:text=Give%20us%20feedback%20in%20our%20Product%20Forums. que direciona para a seção Envie feedback nos nossos Fóruns de produtos contém um fragmento de elemento (HTML1) e um fragmento de texto (text=Give%20us%20feedback%20in%20our%20Product%20Forums.):

Vinculação com fragmento de elemento e fragmento de texto.

A diretiva de fragmento

Há um elemento da sintaxe que ainda não expliquei: a diretiva de fragmento :~:. Para evitar problemas de compatibilidade com fragmentos de elementos de URL existentes, como mostrado, a especificação de fragmentos de texto introduz a diretiva de fragmento. A diretiva de fragmento é uma parte do fragmento do URL delimitada pela sequência de código :~:. Ele é reservado para instruções do user agent, como text=, e é removido do URL durante o carregamento para que os scripts do autor não possam interagir diretamente com ele. As instruções do user agent também são chamadas de diretivas. No caso concreto, text= é chamado de diretiva de texto.

Detecção de recursos

Para detectar a compatibilidade, teste a propriedade fragmentDirective somente leitura em document. A diretiva de fragmento é um mecanismo para que os URLs especifiquem instruções direcionadas ao navegador, e não ao documento. Ele serve para evitar a interação direta com o script do autor, de modo que as instruções futuras do user agent possam ser adicionadas sem o risco de introduzir alterações interruptivas no conteúdo atual. Um exemplo de adição futura são as dicas de tradução.

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

A detecção de recursos é destinada principalmente a casos em que os links são gerados dinamicamente (por exemplo, por mecanismos de pesquisa) para evitar o envio de links de fragmentos de texto para navegadores que não são compatíveis com eles.

Como definir o estilo de fragmentos de texto

Por padrão, os navegadores estilizam fragmentos de texto da mesma forma que estilizam mark (normalmente preto em amarelo, as cores do sistema CSS para mark). A folha de estilo do user agent contém CSS como este:

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

Como você pode ver, o navegador expõe um pseudosseletor ::target-text que pode ser usado para personalizar o destaque aplicado. Por exemplo, você pode criar fragmentos de texto pretos em um fundo vermelho. Como sempre, verifique o contraste de cores para que o estilo de substituição não cause problemas de acessibilidade e garanta que o destaque realmente se sobressaia visualmente em relação ao restante do conteúdo.

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

Polyfill

O recurso de fragmentos de texto pode ser polyfilled até certo ponto. Oferecemos um polyfill, que é usado internamente pela extensão, para navegadores que não oferecem suporte integrado a fragmentos de texto em que a funcionalidade é implementada em JavaScript.

O polyfill contém um arquivo fragment-generation-utils.js que pode ser importado e usado para gerar links de fragmentos de texto. Isso é descrito no exemplo de código:

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);
}

Obter fragmentos de texto para fins de análise

Muitos sites usam o fragmento para roteamento. Por isso, os navegadores removem os fragmentos de texto para não corromper essas páginas. Há uma necessidade reconhecida de expor links de fragmentos de texto para páginas, por exemplo, para fins de análise, mas a solução proposta ainda não foi implementada. Como alternativa, use o código a seguir para extrair informações.

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

Segurança

As diretivas de fragmento de texto são invocadas apenas em navegações completas (não na mesma página) que são o resultado de uma ativação do usuário. Além disso, as navegações que se originam de uma origem diferente do destino exigem que a navegação ocorra em um contexto noopener, de modo que a página de destino seja suficientemente isolada. As diretivas de fragmento de texto são aplicadas apenas ao frame principal. Isso significa que o texto não é pesquisado dentro de iframes, e a navegação em iframes não invoca um fragmento de texto.

Privacidade

É importante que as implementações da especificação de fragmentos de texto não vazem se um fragmento de texto foi encontrado em uma página ou não. Enquanto os fragmentos de elemento estão totalmente sob o controle do autor da página original, os fragmentos de texto podem ser criados por qualquer pessoa. Lembra que, no meu exemplo, não havia como criar um link para o título Módulos ECMAScript em Web Workers, já que o <h1> não tinha um id, mas como qualquer pessoa, inclusive eu, poderia criar um link para qualquer lugar criando cuidadosamente o fragmento de texto?

Imagine que eu administre uma rede de publicidade maliciosa evil-ads.example.com. Imagine também que, em um dos meus iframes de anúncio, eu tenha criado dinamicamente um iframe oculto entre origens para dating.example.com com um URL de fragmento de texto dating.example.com#:~:text=Log%20Out quando o usuário interage com o anúncio. Se o texto "Sair" for encontrado, sei que a vítima está conectada ao dating.example.com, que posso usar para criar um perfil de usuário. Como uma implementação simples de fragmentos de texto pode decidir que uma correspondência bem-sucedida deve causar uma mudança de foco, em evil-ads.example.com, eu poderia detectar o evento blur e, assim, saber quando uma correspondência ocorreu. No Chrome, implementamos os fragmentos de texto de modo que esse cenário não possa acontecer.

Outro ataque pode ser explorar o tráfego de rede com base na posição de rolagem. Suponha que eu tenha acesso aos registros de tráfego de rede da minha vítima, como o administrador de uma intranet corporativa. Agora imagine que exista um longo documento de recursos humanos O que fazer se você sofrer de... e, em seguida, uma lista de condições como burnout, ansiedade etc. Eu poderia colocar um pixel de rastreamento ao lado de cada item da lista. Se eu determinar que o carregamento do documento ocorre temporariamente com o carregamento do pixel de rastreamento ao lado, por exemplo, do item burn out, posso, como administrador da intranet, determinar que um funcionário clicou em um link de fragmento de texto com :~:text=burn%20out que o funcionário pode ter assumido que era confidencial e não visível para ninguém. Como esse exemplo é um pouco artificial e a exploração dele exige que condições prévias muito específicas sejam atendidas, a equipe de segurança do Chrome avaliou que o risco de implementar a rolagem na navegação é gerenciável. Outros user agents podem decidir mostrar um elemento da interface de rolagem manual.

Para sites que querem desativar o recurso, o Chromium oferece um valor de cabeçalho de Política de Documentos que pode ser enviado para que os user agents não processem URLs de fragmentos de texto.

Document-Policy: force-load-at-top

Como desativar fragmentos de texto

A maneira mais fácil de desativar o recurso é usar uma extensão que possa injetar cabeçalhos de resposta HTTP, por exemplo, ModHeader (não é um produto do Google), para inserir um cabeçalho de resposta (não de solicitação) da seguinte maneira:

Document-Policy: force-load-at-top

Outra maneira, mais complexa, de desativar é usar a configuração empresarial ScrollToTextFragmentEnabled. Para fazer isso no macOS, cole o seguinte comando no terminal.

defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false

No Windows, siga a documentação no site de suporte da Ajuda do Google Chrome Enterprise.

Para algumas pesquisas, o mecanismo de pesquisa do Google fornece uma resposta rápida ou um resumo com um snippet de conteúdo de um site relevante. Esses trechos em destaque têm mais chances de aparecer quando uma pesquisa é feita em forma de pergunta. Clicar em um trecho em destaque direciona o usuário diretamente ao texto do trecho em destaque na página da Web de origem. Isso funciona graças aos URLs de fragmentos de texto criados automaticamente.

Página de resultados da pesquisa do Google mostrando um trecho em destaque. A barra de status mostra o URL dos fragmentos de texto.
Depois de clicar, a seção relevante da página é mostrada.

Conclusão

O URL de fragmentos de texto é um recurso poderoso para vincular a textos arbitrários em páginas da Web. A comunidade acadêmica pode usá-lo para fornecer links de citação ou links de referência altamente precisos. Os mecanismos de pesquisa podem usá-lo para criar links diretos para resultados de texto em páginas. Os sites de redes sociais podem usá-lo para permitir que os usuários compartilhem trechos específicos de uma página da Web em vez de capturas de tela inacessíveis. Espero que você comece a usar URLs de fragmento de texto e os ache tão úteis quanto eu. Instale a extensão do navegador Link para fragmento de texto.

Agradecimentos

Os fragmentos de texto foram implementados e especificados por Nick Burris e David Bokan, com contribuições de Grant Wang. Agradecemos a Joe Medley pela revisão completa deste documento.