Vínculo en negrita donde nadie se vinculó antes: Fragmentos de texto

Los fragmentos de texto te permiten especificar un fragmento de texto en el fragmento de URL. Cuando se navega a una URL con un fragmento de texto de este tipo, el navegador puede enfatizarlo o llamar la atención del usuario.

Chrome 80 fue un gran lanzamiento. Contenía una serie de funciones muy esperadas, como módulos de ECMAScript en Web Workers, combinación nula, encadenamiento opcional y mucho más. Como de costumbre, el lanzamiento se anunció a través de una entrada de blog en el blog de Chromium. Puedes ver un extracto de la entrada de blog en la captura de pantalla que aparece a continuación.

Entrada de blog de Chromium con cuadros rojos alrededor de elementos con un atributo id.

Es probable que te preguntes qué significan todas las casillas rojas. Son el resultado de ejecutar el siguiente fragmento en las Herramientas para desarrolladores. Destaca todos los elementos que tienen un atributo id.

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

Puedo colocar un vínculo directo a cualquier elemento destacado con un cuadro rojo gracias al identificador de fragmento, que luego uso en el hash de la URL de la página. Suponiendo que quería incluir un vínculo directo al cuadro Envíanos tus comentarios en nuestros Foros de productos que se encuentra a un lado, puedo hacerlo manualmente con la URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1. Como puedes ver en el panel Elementos de las herramientas para desarrolladores, el elemento en cuestión tiene un atributo id con el valor HTML1.

Herramientas para desarrolladores que muestran el id de un elemento.

Si analizo esta URL con el constructor URL() de JavaScript, se revelan los diferentes componentes. Observa la propiedad hash con el 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: ""
}
*/

Sin embargo, el hecho de que tuve que abrir las herramientas para desarrolladores para encontrar el id de un elemento habla mucho sobre la probabilidad de que el autor de la entrada de blog haya querido vincular esta sección en particular de la página.

¿Qué sucede si quiero establecer un vínculo con un elemento sin un id? Supongamos que quiero vincular al encabezado Módulos de ECMAScript en Web Workers. Como puedes ver en la siguiente captura de pantalla, el <h1> en cuestión no tiene un atributo id, lo que significa que no hay forma de vincularlo a este encabezado. Este es el problema que resuelven los fragmentos de texto.

Herramientas para desarrolladores que muestran un encabezado sin un id.

Fragmentos de texto

La propuesta de Fragmentos de texto agrega compatibilidad para especificar un fragmento de texto en el hash de URL. Cuando se navega a una URL con un fragmento de texto de este tipo, el agente de usuario puede enfatizarlo o llamar la atención del usuario.

Compatibilidad del navegador

Navegadores compatibles

  • Chrome: 89.
  • Edge: 89.
  • Firefox: 131
  • Versión preliminar de tecnología de Safari: Compatible.

Origen

Por motivos de seguridad, la función requiere que los vínculos se abran en un contexto noopener. Por lo tanto, asegúrate de incluir rel="noopener" en tu marcado de ancla <a> o agrega noopener a tu lista Window.open() de funciones de la funcionalidad de la ventana.

start

En su forma más simple, la sintaxis de los fragmentos de texto es la siguiente: el símbolo de hash # seguido de :~:text= y, por último, start, que representa el texto codificado en porcentaje al que quiero vincular.

#:~:text=start

Por ejemplo, supongamos que quiero vincular al encabezado Módulos de ECMAScript en Web Workers en la entrada de blog en la que se anuncian las funciones de Chrome 80. En este caso, la URL sería la siguiente:

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

El fragmento de texto se enfatiza de esta manera. Si haces clic en el vínculo en un navegador compatible, como Chrome, el fragmento de texto se destacará y se desplazará hasta que esté a la vista:

Fragmento de texto desplazado a la vista y destacado.

start y end

Ahora bien, ¿qué sucede si quiero vincular a toda la sección titulada Módulos de ECMAScript en trabajadores web, no solo a su encabezado? Si codificas con porcentaje todo el texto de la sección, la URL resultante sería demasiado larga para ser práctica.

Por suerte, existe una mejor manera. En lugar de todo el texto, puedo enmarcar el texto deseado con la sintaxis start,end. Por lo tanto, especifiqué un par de palabras con codificación porcentual al comienzo del texto deseado y un par de palabras con codificación porcentual al final del texto deseado, separadas por una coma ,.

Se ve de la siguiente manera:

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, tengo ECMAScript%20Modules%20in%20Web%20Workers, luego una coma , seguida de ES%20Modules%20in%20Web%20Workers. como end. Cuando haces clic en un navegador compatible, como Chrome, se destaca toda la sección y se desplaza hasta que se muestra:

Fragmento de texto desplazado a la vista y destacado.

Ahora te preguntarás por qué elegí start y end. En realidad, también habría funcionado la URL ligeramente más corta https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules,Web%20Workers. con solo dos palabras en cada lado. Compara start y end con los valores anteriores.

Si doy un paso más y ahora uso solo una palabra para start y end, puedes ver que tengo problemas. La URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript,Workers. ahora es aún más corta, pero el fragmento de texto destacado ya no es el que se deseaba originalmente. El resaltado se detiene en la primera aparición de la palabra Workers., lo cual es correcto, pero no es lo que quería resaltar. El problema es que la sección deseada no se identifica de forma única mediante los valores actuales de start y end de una palabra:

Fragmento de texto no deseado que se desplazó hasta a la vista y se destacó.

prefix- y -suffix

Usar valores lo suficientemente largos para start y end es una solución para obtener un vínculo único. Sin embargo, en algunas situaciones, esto no es posible. A modo de nota al margen, ¿por qué elegí la entrada de blog de lanzamiento de Chrome 80 como ejemplo? La respuesta es que, en esta versión, se introdujeron los fragmentos de texto:

Texto de la entrada de blog: Fragmentos de URL de texto. Los usuarios o autores ahora pueden vincular a una parte específica de una página usando un fragmento de texto proporcionado en una URL. Cuando se carga la página, el navegador destaca el texto y desplaza el fragmento hasta la vista. Por ejemplo, la siguiente URL carga una página de wiki sobre &quot;Gato&quot; y se desplaza hasta el contenido que aparece en el parámetro &quot;text&quot;.
Extracto de la entrada de blog del anuncio de los fragmentos de texto.

Observa cómo, en la captura de pantalla anterior, la palabra "text" aparece cuatro veces. La cuarta ocurrencia está escrita en una fuente de código verde. Si quisiera vincular a esta palabra en particular, establecería start en text. Como la palabra "text" es, bueno, solo una palabra, no puede haber un end. ¿Y ahora qué? La URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=text coincide con la primera aparición de la palabra "Text" que ya está en el encabezado:

Coincidencia de fragmento de texto en la primera aparición de "Texto".

Por suerte, hay una solución. En casos como este, puedo especificar una prefix​- y una -suffix. La palabra antes de la fuente de código verde “text” es “the” y la palabra después es “parameter”. Ninguna de las otras tres ocurrencias de la palabra "text" tiene las mismas palabras circundantes. Con este conocimiento, puedo modificar la URL anterior y agregar prefix- y -suffix. Al igual que los otros parámetros, también deben estar codificados en porcentaje y pueden contener más de una palabra. https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=the-,text,-parameter. Para permitir que el analizador identifique claramente el prefix- y el -suffix, deben separarse del start y el end opcional con un guion -.

Fragmento de texto que coincide con la ocurrencia deseada de "text".

La sintaxis completa

A continuación, se muestra la sintaxis completa de los fragmentos de texto. (Los corchetes indican un parámetro opcional). Los valores de todos los parámetros deben estar codificados como porcentaje. Esto es de gran importancia para los caracteres de guion -, signo & & y coma ,, de modo que no se interpreten como parte de la sintaxis de la directiva de texto.

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

Cada uno de prefix-, start, end y -suffix solo coincidirá con el texto dentro de un solo elemento a nivel del bloque, pero los rangos completos de start,end pueden abarcar varios bloques. Por ejemplo, :~:text=The quick,lazy dog no coincidirá en el siguiente ejemplo porque la string inicial "The fast" no aparece dentro de un único elemento a nivel de bloque sin interrupciones:

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

Sin embargo, coincide en este ejemplo:

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

Cómo crear URLs de fragmentos de texto con una extensión de navegador

Crear URLs de fragmentos de texto de forma manual es tedioso, en especial cuando se trata de asegurarse de que sean únicas. Si realmente lo deseas, en la especificación se incluyen algunas sugerencias y se enumeran los pasos exactos para generar URLs de fragmentos de texto. Proporcionamos una extensión de navegador de código abierto llamada Link to Text Fragment que te permite vincular cualquier texto. Para ello, selecciónalo y, luego, haz clic en "Copy Link to Selected Text" en el menú contextual. Esta extensión está disponible para los siguientes navegadores:

Extensión del navegador para vincular a un fragmento de texto

Varios fragmentos de texto en una URL

Ten en cuenta que pueden aparecer varios fragmentos de texto en una URL. Los fragmentos de texto en particular deben estar separados por un carácter & &. A continuación, se incluye un vínculo de ejemplo con tres 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.

Tres fragmentos de texto en una URL.

Cómo combinar fragmentos de elementos y texto

Los fragmentos de elementos tradicionales se pueden combinar con fragmentos de texto. No hay problema en tener ambos en la misma URL, por ejemplo, para proporcionar un resguardo significativo en caso de que cambie el texto original de la página, de modo que el fragmento de texto ya no coincida. La URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1:~:text=Give%20us%20feedback%20in%20our%20Product%20Forums. que dirige a la sección Envíanos tus comentarios en nuestros Foros de productos contiene un fragmento de elemento (HTML1) y un fragmento de texto (text=Give%20us%20feedback%20in%20our%20Product%20Forums.):

Vinculación con el fragmento de elemento y el fragmento de texto.

La directiva de fragmento

Hay un elemento de la sintaxis que aún no he explicado: la directiva de fragmento :~:. Para evitar problemas de compatibilidad con los fragmentos de elementos de URL existentes, como se muestra más arriba, la especificación de fragmentos de texto presenta la directiva de fragmento. La directiva de fragmento es una parte del fragmento de URL delimitado por la secuencia de código :~:. Se reserva para las instrucciones del usuario-agente, como text=, y se quita de la URL durante la carga para que las secuencias de comandos del autor no puedan interactuar directamente con ella. Las instrucciones del usuario-agente también se denominan directivas. En el caso concreto, text= se denomina directiva de texto.

Detección de funciones

Para detectar la compatibilidad, prueba la propiedad fragmentDirective de solo lectura en document. La directiva de fragmento es un mecanismo para que las URLs especifiquen instrucciones dirigidas al navegador en lugar del documento. Está diseñado para evitar la interacción directa con la secuencia de comandos del autor, de modo que se puedan agregar instrucciones futuras del usuario-agente sin temor a introducir cambios rotundos en el contenido existente. Un posible ejemplo de esas incorporaciones futuras podrían ser las sugerencias de traducción.

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

La detección de funciones está diseñada principalmente para casos en los que los vínculos se generan de forma dinámica (por ejemplo, a través de motores de búsqueda) para evitar la entrega de vínculos de fragmentos de texto en navegadores que no los admiten.

Cómo aplicar estilo a fragmentos de texto

De forma predeterminada, los navegadores aplican estilos a los fragmentos de texto de la misma manera que a mark (por lo general, negro sobre amarillo, los colores del sistema de CSS para mark). La hoja de estilo del usuario-agente contiene CSS que se ve de la siguiente manera:

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

Como puedes ver, el navegador expone un pseudoselector ::target-text que puedes usar para personalizar el resaltado aplicado. Por ejemplo, puedes diseñar tus fragmentos de texto para que sean de color negro sobre un fondo rojo. Como siempre, asegúrate de comprobar el contraste de color para que el diseño de anulación no cause problemas de accesibilidad y asegúrate de que el elemento destacado realmente se destaque visualmente del resto del contenido.

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

Compatibilidad con el complemento depolyfill

La función de fragmentos de texto se puede reemplazar en cierta medida. Proporcionamos un polyfill, que la extensión usa de forma interna, para los navegadores que no proporcionan compatibilidad integrada con fragmentos de texto en los que la funcionalidad se implementa en JavaScript.

El polyfill contiene un archivo fragment-generation-utils.js que puedes importar y usar para generar vínculos de fragmentos de texto. Esto se describe en la siguiente muestra 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);
}

Cómo obtener fragmentos de texto con fines estadísticos

Muchos sitios usan el fragmento para el enrutamiento, por lo que los navegadores quitan los fragmentos de texto para no dañar esas páginas. Existe una necesidad reconocida de exponer vínculos de fragmentos de texto a páginas, por ejemplo, con fines de estadísticas, pero la solución propuesta aún no se implementa. Como solución alternativa por el momento, puedes usar el siguiente código para extraer la información deseada.

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

Seguridad

Las directivas de fragmentos de texto solo se invocan en navegaciones completas (que no pertenecen a la misma página) que sean el resultado de una activación del usuario. Además, las navegaciones que se originen en un origen diferente del destino requerirán que estas se realicen en un contexto de noopener, de modo que se sepa que la página de destino está lo suficientemente aislada. Las directivas de fragmento de texto solo se aplican al marco principal. Esto significa que no se buscará texto dentro de los iframes, y la navegación en iframes no invocará un fragmento de texto.

Privacidad

Es importante que las implementaciones de la especificación de fragmentos de texto no filtren si se encontró un fragmento de texto en una página o no. Si bien los fragmentos de elementos están completamente bajo el control del autor de la página original, cualquier persona puede crear fragmentos de texto. Recuerda que, en mi ejemplo anterior, no había forma de vincular al encabezado ECMAScript Modules in Web Workers, ya que <h1> no tenía un id, pero ¿cómo cualquiera, incluido yo, podría vincular a cualquier lugar si se elaboraba con cuidado el fragmento de texto?

Imagina que ejecuté una red de publicidad maliciosa evil-ads.example.com. Además, imagina que, en uno de mis iframes de anuncios, creé de forma dinámica un iframe oculto de origen cruzado para dating.example.com con una URL de fragmento de texto dating.example.com#:~:text=Log%20Out una vez que el usuario interactúa con el anuncio. Si se encuentra el texto “Salir”, sé que la víctima está accediendo a dating.example.com, lo que podría usar para crear perfiles de usuario. Dado que una implementación ingenua de Text Fragments podría decidir que una coincidencia correcta debe provocar un cambio de enfoque, en evil-ads.example.com, podría detectar el evento blur y, así, saber cuándo se produjo una coincidencia. En Chrome, implementamos los fragmentos de texto de manera tal que no pueda ocurrir la situación anterior.

Otro ataque podría ser aprovechar el tráfico de red según la posición de desplazamiento. Supongamos que tengo acceso a los registros de tráfico de red de mi víctima, como el administrador de la intranet de una empresa. Ahora imagina que existe un largo documento de recursos humanos Qué hacer si sufres de… y, luego, una lista de afecciones como agotamiento, ansiedad, etcétera. Podría colocar un píxel de seguimiento junto a cada elemento de la lista. Si, luego, determino que la carga del documento ocurre de forma simultánea con la carga del píxel de seguimiento junto a, por ejemplo, el elemento agotamiento, como administrador de la intranet, puedo determinar que un empleado hizo clic en un vínculo de fragmento de texto con :~:text=burn%20out que podría haber supuesto que era confidencial y que nadie podía ver. Como este ejemplo es un poco forzado al principio y dado que su explotación requiere que se cumplan condiciones previas muy específicas, el equipo de seguridad de Chrome evaluó el riesgo de implementar el desplazamiento en la navegación para que sea administrable. Otros usuarios-agentes pueden decidir mostrar un elemento de IU de desplazamiento manual.

En el caso de los sitios que deseen inhabilitar esta función, Chromium admite un valor de encabezado de política de documentos que pueden enviar para que los usuarios-agentes no procesen las URLs de fragmentos de texto.

Document-Policy: force-load-at-top

Cómo inhabilitar fragmentos de texto

La forma más fácil de inhabilitar la función es usar una extensión que pueda insertar encabezados de respuesta HTTP, por ejemplo, ModHeader (no es un producto de Google) para insertar un encabezado de respuesta (no solicitud) de la siguiente manera:

Document-Policy: force-load-at-top

Otra forma de inhabilitar esta opción, que está más involucrada, es mediante el uso de la configuración empresarial ScrollToTextFragmentEnabled. Para hacerlo en macOS, pega el siguiente comando en la terminal.

defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false

En Windows, sigue la documentación del sitio de asistencia de la Ayuda de Google Chrome Enterprise.

En algunas búsquedas, el motor de búsqueda Google proporciona una respuesta rápida o un resumen con un fragmento de contenido de un sitio web relevante. Es más probable que estos fragmentos destacados aparezcan cuando una búsqueda tiene la forma de una pregunta. Cuando un usuario hace clic en un fragmento destacado, se lo dirige directamente al texto correspondiente en la página web de origen. Esto funciona gracias a las URLs de fragmentos de texto creados automáticamente.

Página de resultados del motor de búsqueda de Google que muestra un fragmento destacado. La barra de estado muestra la URL de los fragmentos de texto.
Después de hacer clic, se desplaza la sección relevante de la página para que se muestre.

Conclusión

La URL de fragmentos de texto es una función potente para vincular a texto arbitrario en páginas web. La comunidad académica puede usarlo para proporcionar vínculos de cita o referencia muy precisos. Los motores de búsqueda pueden usarlo para crear vínculos directos a resultados de texto en páginas. Los sitios de redes sociales pueden usarlo para permitir que los usuarios compartan fragmentos específicos de una página web en lugar de capturas de pantalla inaccesibles. Espero que comiences a usar URLs de fragmentos de texto y que te resulten tan útiles como a mí. Asegúrate de instalar la extensión del navegador Link to Text Fragment.

Agradecimientos

Nick Burris y David Bokan implementaron y especificaron Text Fragments, con contribuciones de Grant Wang. Agradecemos a Joe Medley por la revisión exhaustiva de este artículo. Imagen hero de Greg Rakozy en Unsplash.