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

Os fragmentos de texto permitem especificar um snippet de texto no fragmento de URL. Ao navegar para um URL com esse fragmento de texto, o navegador pode enfatizar e/ou chamar a atenção do usuário.

O Chrome 80 foi uma grande versão. Ele continha uma série de recursos muito esperados, como módulos ECMAScript em Web Workers, coalescência nula, encadeamento opcional e muito mais. O lançamento foi, como de costume, anunciado em uma postagem do blog (link em inglês) no blog do Chromium. Confira um trecho da postagem do blog na captura de tela abaixo.

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

Você provavelmente está se perguntando o que significam as caixas vermelhas. Elas são o resultado da execução do fragmento abaixo no DevTools. Ele destaca todos os elementos que têm um atributo id.

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

É possível colocar um link direto para qualquer elemento destacado com uma caixa vermelha usando o identificador de fragmento, que é usado no hash do URL da página. Supondo que eu quisesse criar um link direto para a caixa Envie seu feedback no Fóruns de produtos no lado, eu 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 do desenvolvedor mostrando a 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: ""
}
*/

O fato de eu ter que abrir as ferramentas do desenvolvedor para encontrar o id de um elemento fala muito sobre a probabilidade de essa seção específica da página ser vinculada pelo autor da postagem do blog.

E se eu quiser vincular a um site sem id? Digamos que eu queira vincular o título ECMAScript Modules in Web Workers. Como mostrado na captura de tela abaixo, o <h1> em questão não tem um atributo id, o que significa que não há como vincular esse cabeçalho. 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 trecho de texto no hash do URL. Ao navegar para um URL com esse fragmento, o user agent pode enfatizá-lo e/ou mostrá-lo à atenção do usuário.

Compatibilidade com navegadores

Compatibilidade com navegadores

  • Chrome: 89.
  • Borda: 89.
  • Firefox: 131.
  • Safari Technology Preview: compatível.

Origem

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

start

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

#:~:text=start

Por exemplo, digamos que eu queira vincular o título ECMAScript Modules in Web Workers na postagem do blog que anuncia os recursos do Chrome 80. O URL nesse caso seria:

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

O fragmento de texto é enfatizado desta maneira. Se você clicar no link em um navegador compatível, como o Chrome, o fragmento de texto será destacado e rolado para aparecer:

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 nos Web Workers, não apenas o cabeçalho? A codificação de percentual de todo o texto da seção tornaria o URL resultante longo demais para ser prático.

Felizmente, há uma maneira melhor. Em vez de todo o texto, posso enquadrar o texto desejado usando a sintaxe start,end. Portanto, especifiquei algumas palavras codificadas por porcentagem no início do texto desejado e outras no final, separadas por uma vírgula ,.

Ela tem esta aparência:

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 aparecer:

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

Você pode estar se perguntando 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 um passo adiante e usar apenas uma palavra para start e end, você vai perceber que estou com problemas. O URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript,Workers. está ainda mais curto, mas o fragmento de texto destacado não é mais o desejado originalmente. O destaque para na primeira ocorrência da palavra Workers., que é o correto, mas não é o que eu pretendi destacar. O problema é que a seção desejada não é identificada exclusivamente pelos valores atuais de start e end de uma palavra:

Fragmento de texto não pretendido rolado para a área de 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, isso não é possível em algumas situações. Por que escolhi a postagem do blog de 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 do texto. Agora, os usuários ou autores podem criar links para 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 apareça. Por exemplo, o URL abaixo carrega uma página wiki para &quot;Gato&quot; e rola até o conteúdo listado no parâmetro &quot;texto&quot;.
Texto do post do blog sobre o anúncio de fragmentos de texto.

Observe que, na captura de tela acima, a palavra "text" aparece quatro vezes. A quarta ocorrência é gravada em uma fonte de código verde. Se eu quisesse vincular a essa palavra específica, definiria start como text. Como a palavra "text" é 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" que já está no título:

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

Felizmente, há uma solução. Em casos como esse, posso especificar um prefix​- e um -suffix. A palavra antes da fonte de código verde "texto" é "o" e a palavra depois é "parâmetro". Nenhuma das outras três ocorrências da palavra "text" 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 em percentual 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 desejada 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 em percentual. Isso é especialmente importante para os caracteres traço -, ampersand & e vírgula ,, para que não sejam interpretados como parte da sintaxe do diretivo de texto.

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

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

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

No entanto, ele corresponde a este 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 de 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 traz algumas dicas e lista as etapas para gerar URLs de fragmento de texto. Fornecemos uma extensão de navegador de código aberto chamada Link para fragmento de texto que permite criar um link para 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 to Text Fragment 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 precisam ser separados por um caractere & &. Confira 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.

Misturando fragmentos de elementos e texto

Os fragmentos de elementos tradicionais podem ser combinados com fragmentos de texto. É perfeitamente aceitável ter os dois no mesmo URL, por exemplo, para fornecer um substituto útil 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 vincula à seção Envie feedback nos fóruns de produtos contém um fragmento de elemento (HTML1) e um fragmento de texto (text=Give%20us%20feedback%20in%20our%20Product%20Forums.):

Vincular com o fragmento de elemento e o fragmento de texto.

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 já existentes, como mostrado acima, a especificação de fragmentos de texto introduz a diretiva de fragmento. A diretiva de fragmento é uma parte do fragmento de URL delimitado 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 de 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 no document. A diretiva de fragmento é um mecanismo para URLs especificarem instruções direcionadas ao navegador, e não ao documento. O objetivo é evitar a interação direta com o script do autor para que as instruções do agente do usuário futuras possam ser adicionadas sem medo de introduzir mudanças importantes no conteúdo atual. Um exemplo possível de tais adições futuras são as dicas de tradução.

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

A detecção de recursos é principalmente destinada a casos em que os links são gerados dinamicamente (por exemplo, por mecanismos de pesquisa) para evitar a veiculação de links de fragmentos de texto para navegadores que não oferecem suporte a 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 do CSS para mark). A folha de estilo do user-agent contém CSS parecido com este:

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

Como você pode ver, o navegador expõe um pseudoseletor ::target-text que pode ser usado para personalizar o realce aplicado. Por exemplo, você pode projetar seus fragmentos de texto para serem 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 para garantir que o destaque realmente se destaque visualmente do restante do conteúdo.

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

Capacidade de polivalidade

Até certo ponto, o recurso de fragmentos de texto pode receber polyfill. Fornecemos 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 abaixo:

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

Como conseguir fragmentos de texto para fins de análise

Muitos sites usam o fragmento para roteamento, e é por isso que os navegadores removem os fragmentos de texto para não quebrar 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 solução alternativa, por enquanto, use o código abaixo para extrair as informações desejadas.

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 (diferentes da página atual) que são o resultado de uma ativação do usuário. Além disso, as navegações originadas 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 isolada o suficiente. As diretivas de fragmento de texto são aplicadas apenas ao frame principal. Isso significa que o texto não será pesquisado dentro de iframes, e a navegação do iframe não vai invocar 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. Embora os fragmentos de elementos estejam totalmente sob o controle do autor da página original, os fragmentos de texto podem ser criados por qualquer pessoa. Lembra como no meu exemplo acima não havia como vincular ao título ECMAScript Modules in Web Workers, já que o <h1> não tinha um id, mas como alguém, incluindo eu, poderia vincular a qualquer lugar criando cuidadosamente o fragmento de texto?

Imagine que eu gerenciasse uma rede de publicidade mal-intencionada evil-ads.example.com. Além disso, imagine que em um dos meus iframes de anúncios, criei dinamicamente um iframe oculto de origem cruzada 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 "Log Out" for encontrado, sei que a vítima está conectada a dating.example.com, que posso usar para criar o perfil do usuário. Como uma implementação de fragmentos de texto simples 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 saber quando uma correspondência ocorreu. No Chrome, implementamos os fragmentos de texto de modo que o cenário acima não possa acontecer.

Outro ataque pode ser a exploração do tráfego de rede com base na posição de rolagem. Vamos supor que eu tenha acesso aos registros de tráfego de rede da vítima, como o administrador da intranet de uma empresa. Agora imagine que exista um longo documento de recursos humanos O que fazer se você sofre de… e uma lista de condições, como esgotamento, ansiedade etc. Poderia colocar um pixel de rastreamento ao lado de cada item na lista. Se eu determinar que o carregamento do documento ocorre ao mesmo tempo que o carregamento do pixel de rastreamento ao lado de, digamos, o 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 provavelmente presumiu ser confidencial e não visível para ninguém. Como esse exemplo é um pouco artificial para começar e, como a exploração dele exige condições muito específicas, a equipe de segurança do Chrome avaliou que o risco de implementar o rolagem na navegação é gerenciável. Outros user agents podem decidir mostrar um elemento de interface de rolagem manual.

Para sites que não querem participar, o Chromium oferece suporte a um valor de cabeçalho Document Policy que pode ser enviado para que os user agents não processem URLs de fragmento 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 pode injetar cabeçalhos de resposta HTTP, por exemplo, ModHeader (não é um produto do Google) para inserir um cabeçalho de resposta (não 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 comando abaixo no terminal.

defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false

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

Em algumas pesquisas, o mecanismo de pesquisa do Google fornece uma resposta rápida ou um resumo com um trecho de conteúdo de um site relevante. Esses trechos em destaque têm mais chances de aparecer quando uma pesquisa é feita na forma de uma 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 do mecanismo de pesquisa do Google mostrando um snippet em destaque. A barra de status mostra o URL dos fragmentos de texto.
Depois de clicar, a seção relevante da página é rolada para aparecer.

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á-los para fornecer citações ou links de referência altamente precisos. Os mecanismos de pesquisa podem usá-lo para criar links diretos para resultados de texto nas páginas. Os sites de rede social podem usar essa opção 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 fragmentos de texto e os considere tão úteis quanto eu. Instale a extensão Link to Text Fragment para o navegador.

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 artigo. Imagem principal de Greg Rakozy no Unsplash.