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 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.

Identificadores de fragmento

O Chrome 80 foi um grande lançamento. Ele continha vários recursos muito esperados, como Módulos ECMAScript em Web Workers, coalescência anulada, 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. Veja 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 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. Supondo que eu queira um link direto para a caixa Envie feedback em nossos Fóruns de produtos, eu poderia fazer isso criando o URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1. Como é possível observar 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 ter que abrir as Ferramentas para desenvolvedores para encontrar o id de um elemento diz muito sobre a probabilidade de essa seção específica da página ter sido criada pelo autor da postagem do blog.

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

Dev Tools mostrando um cabeçalho sem id.

Fragmentos de texto

A proposta Text Fragments adiciona suporte à especificação de 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/ou chamar a atenção dele.

Compatibilidade com navegadores

Compatibilidade com navegadores

  • 89
  • 89
  • x
  • x

Origem

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

start

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

#:~:text=start

Por exemplo, digamos que eu queira um link para o título Módulos ECMAScript in Web Workers na postagem do blog que anuncia recursos no Chrome 80, o URL neste 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 assim. Se você clicar no link em um navegador compatível, como o Chrome, o fragmento de texto será destacado e será exibido na tela:

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

start e end

E se eu quiser vincular a toda a seção intitulada Módulos ECMAScript in Web Workers, não apenas o cabeçalho? A codificação por porcentagem de todo o texto da seção tornaria o URL resultante muito longo.

Felizmente, existe uma maneira melhor. Em vez do texto inteiro, posso enquadrar o texto desejado usando a sintaxe start,end. Portanto, é preciso especificar algumas palavras codificadas por porcentagem no início do texto desejado e algumas palavras codificadas por porcentagem no final dele, separadas por vírgula ,.

O resultado é este:

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, a seção inteira é 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 funcionaria. Compare start e end com os valores anteriores.

Se eu der um passo adiante e usar apenas uma palavra para start e end, você verá 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 agora, mas o fragmento de texto destacado não é mais o pretendido originalmente. O destaque é interrompido na primeira ocorrência da palavra Workers., que está correta, mas não é o que eu pretendia destacar. O problema é que a seção desejada não é identificada exclusivamente pelos valores atuais de uma palavra start e end:

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

prefix- e -suffix

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

Texto da postagem do blog: Fragmentos de URL de texto. Agora, 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 exibição. 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 na captura de tela acima a palavra “texto” aparece quatro vezes. A quarta ocorrência é escrita em uma fonte de código verde. Para criar um link para essa palavra específica, defina start como text. Como a palavra "texto" é apenas uma palavra, não pode haver 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" no título:

Correspondência de fragmento de texto na primeira ocorrência de "Text".

Felizmente existe uma solução. Em casos como esse, posso especificar um prefix​- e um -suffix. A palavra antes da fonte do código verde "texto" é "o" e a palavra posterior é "parâmetro". 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. 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 -.

Correspondência de fragmento de texto na ocorrência desejada de "texto".

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 ,. Portanto, eles não estão sendo interpretados como parte da sintaxe da diretiva de texto.

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

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

<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 de navegador

Criar URLs de fragmentos de texto manualmente é entediante, especialmente quando se trata de garantir que eles sejam únicos. Se você realmente quiser, a especificação tem 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 o fragmento de texto, que permite vincular qualquer texto selecionando-o e clicando em "Copiar link para o texto selecionado" no menu de contexto. Esta extensão está disponível para os seguintes navegadores:

Link para a extensão do navegador Text Fragment.

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 "e" comercial &. 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.

Como misturar elementos e fragmentos de texto

Os fragmentos de elementos tradicionais podem ser combinados com fragmentos de texto. Não há problema em ter ambos no mesmo URL, por exemplo, para fornecer um substituto significativo caso o texto original na página seja alterado, 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 na seção 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 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 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 de autor não interajam diretamente com ele. As instruções do user agent também são chamadas de diretivas. No caso concreto, a text= é chamada 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 em vez do documento. O objetivo é evitar a interação direta com o script do autor, para que futuras instruções do user agent possam ser adicionadas sem medo de introduzir alterações interruptivas no conteúdo atual. Um exemplo potencial dessas adições futuras podem ser dicas de tradução.

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

A detecção de recursos é destinada principalmente aos casos em que 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 têm suporte a eles.

Como definir o estilo de fragmentos de texto

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

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

Como você pode notar, o navegador expõe um pseudoseletor ::target-text que pode ser usado para personalizar o destaque aplicado. Por exemplo, você pode projetar fragmentos de texto para serem texto preto em um segundo plano vermelho. Como sempre, verifique o contraste de cores para que o estilo modificado não cause problemas de acessibilidade e confira se o destaque realmente se destaca visualmente do restante do conteúdo.

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

Polifilabilidade

O recurso de fragmentos de texto pode ter polyfill aplicado até certo ponto. Fornecemos um polyfill, 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 você pode importar e usar para gerar links de fragmento 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);
}

Extrair fragmentos de texto para fins de análise

Muitos sites usam o fragmento para roteamento. É por isso que os navegadores removem fragmentos de texto para não corromper essas páginas. Há uma necessidade confirmada 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 (que não sejam da mesma página) resultantes de uma ativação do usuário. Além disso, as navegações originadas de uma origem diferente do destino exigirão que a navegação ocorra em um contexto noopener, de modo que a página de destino tenha um isolamento suficiente. As diretivas de fragmentos 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 de iframe não 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 for encontrado em uma página ou não. Embora os fragmentos de elemento estejam totalmente sob o controle do autor da página original, os fragmentos de texto podem ser criados por qualquer pessoa. Lembre-se de que, no exemplo acima, não havia como criar um link para o título ECMAScript Modules in Web Workers, já que o <h1> não tinha um id, mas como qualquer pessoa, incluindo eu, poderia simplesmente vincular a qualquer lugar elaborando cuidadosamente o fragmento de texto?

Imagine que eu executei uma rede de publicidade ruim evil-ads.example.com. Imagine também que, em um dos meus iframes de anúncio, eu criei dinamicamente um iframe de origem cruzada oculto para dating.example.com com um URL de fragmento de texto dating.example.com#:~:text=Log%20Out quando o usuário interagir com o anúncio. Se o texto "Log Out" for encontrado, isso significa que a vítima está conectada a dating.example.com, o que pode ser usado para criar um perfil de usuário. Como uma implementação simples de fragmentos de texto pode decidir que uma correspondência bem-sucedida causaria uma mudança de foco, no evil-ads.example.com, eu poderia detectar o evento blur e saber quando uma correspondência ocorreu. No Chrome, implementamos fragmentos de texto de modo que o cenário acima não acontece.

Outro ataque pode ser explorar o 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 minha vítima, por exemplo, como administrador da intranet de uma empresa. Agora imagine que existia um longo documento de recursos humanos O que fazer se você sofreu de... e uma lista de condições como burnout, ansiedade etc. Eu poderia colocar um pixel de rastreamento ao lado de cada item na lista. Se eu determinar que o carregamento do documento temporariamente ocorre com o carregamento do pixel de rastreamento ao lado do item de esgotamento, por exemplo, posso, como administrador da intranet, determinar que um funcionário clicou em um link de fragmento de texto com :~:text=burn%20out que ele possa ter considerado confidencial e não visível para ninguém. Como esse exemplo é um pouco complexo no começo e como a exploração dele requer condições muito específicas, a equipe de segurança do Chrome avaliou o risco de implementar a rolagem na navegação para ser gerenciável. Outros user agents podem mostrar um elemento de interface de rolagem manual em vez disso.

Para sites que quiserem desativar, 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 fragmentos de texto.

Document-Policy: force-load-at-top

Como desativar fragmentos de texto

A maneira mais fácil de desativar o recurso é usando 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 é usando 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 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. Estes trechos em destaque têm mais chances de aparecer quando uma pesquisa é feita na forma de uma pergunta. Ao clicar em um trecho em destaque, o usuário é levado diretamente ao texto dele 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 trecho em destaque. A barra de status mostra o URL de fragmentos de texto.
Depois de clicar, a seção relevante da página é exibida na tela após a rolagem.

Conclusão

O URL de fragmentos de texto é um recurso eficiente para vincular textos arbitrários em páginas da Web. A comunidade acadêmica pode usá-los para fornecer citações ou referências de alta precisão. Os mecanismos de pesquisa podem usá-lo para links diretos para resultados de texto nas páginas. 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. Não se esqueça de instalar a extensão de navegador Link to Text Fragment.

Agradecimentos

Os fragmentos de texto foram implementados e especificados por Nick Burris e David Bokan, com contribuições de Grant Wang (links em inglês). Agradecemos a Joe Medley pela revisão completa deste artigo. Imagem principal de Greg Rakozy no Unsplash.