Verlinken, wo noch niemand etwas verknüpft hat: Textfragmente

Mit Textfragmenten können Sie einen Textausschnitt im URL-Fragment angeben. Wenn ein Nutzer eine URL mit einem solchen Textfragment aufruft, kann der Browser es hervorheben und den Nutzer darauf aufmerksam machen.

Fragmentbezeichner

Chrome 80 war ein großes Release. Sie enthielt eine Reihe von mit Spannung erwarteten Funktionen wie ECMAScript-Module in Web Workern, Nullish Coalescing, Optional Chaining und mehr. Die Veröffentlichung wurde wie üblich in einem Blogpost im Chromium-Blog angekündigt. Im folgenden Screenshot sehen Sie einen Auszug aus dem Blogpost.

Chromium-Blogpost mit roten Kästchen um Elemente mit einem id-Attribut.

Sie fragen sich wahrscheinlich, was die roten Kästchen bedeuten. Sie sind das Ergebnis der Ausführung des folgenden Snippets in den Entwicklertools. Alle Elemente mit einem id-Attribut werden hervorgehoben.

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

Ich kann einen Deeplink zu jedem Element platzieren, das mit einem roten Feld hervorgehoben ist. Dazu verwende ich die Fragment-ID, die ich dann im Hash der Seiten-URL verwende. Angenommen, ich möchte einen Deeplink zum Feld Feedback in unseren Produktforen in der Seitenleiste erstellen. Dazu kann ich die URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1 manuell erstellen. Wie Sie im Bereich „Elemente“ der Entwicklertools sehen können, hat das betreffende Element ein id-Attribut mit dem Wert HTML1.

Entwicklertools, die den id eines Elements zeigen.

Wenn ich diese URL mit dem JavaScript-Konstruktor URL() parse, werden die verschiedenen Komponenten angezeigt. Beachten Sie das Attribut hash mit dem Wert #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: ""
}
*/

Die Tatsache, dass ich die Entwicklertools öffnen musste, um die id eines Elements zu finden, spricht Bände über die Wahrscheinlichkeit, dass der Autor des Blogposts auf diesen bestimmten Abschnitt der Seite verlinken wollte.

Was ist, wenn ich einen Link zu etwas ohne id erstellen möchte? Angenommen, ich möchte einen Link zur Überschrift ECMAScript-Module in Web Workern erstellen. Wie Sie im folgenden Screenshot sehen können, hat das betreffende <h1> kein id-Attribut. Das bedeutet, dass ich keinen Link zu dieser Überschrift erstellen kann. Dieses Problem wird durch Textfragmente gelöst.

Entwicklertools, die eine Überschrift ohne id anzeigen.

Textfragmente

Der Vorschlag Textfragmente bietet Unterstützung für die Angabe eines Text-Snippets im URL-Hash. Wenn der User-Agent eine URL mit einem solchen Textfragment aufruft, kann er es hervorheben und den Nutzer darauf aufmerksam machen.

Browser-Kompatibilität

Browser Support

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

Source

Aus Sicherheitsgründen müssen Links in einem noopener-Kontext geöffnet werden. Achten Sie daher darauf, rel="noopener" in Ihr <a>-Ankermarkup aufzunehmen oder noopener in die Liste der Fensterfunktionen von Window.open() aufzunehmen.

start

In der einfachsten Form sieht die Syntax von Textfragmenten so aus: Das Hash-Symbol #, gefolgt von :~:text= und schließlich start, was den prozentcodierten Text darstellt, auf den ich verlinken möchte.

#:~:text=start

Wenn ich beispielsweise auf die Überschrift ECMAScript Modules in Web Workers im Blogpost zu den Funktionen in Chrome 80 verlinken möchte, lautet die URL:

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

Das Textfragment wird so hervorgehoben. Wenn ihr den Link in einem unterstützten Browser wie Chrome anklickt, wird das Textfragment hervorgehoben und in den sichtbaren Bereich gescrollt:

Textfragment, das in die Ansicht gescrollt und hervorgehoben wurde.

start und end

Was ist, wenn ich nicht nur auf die Überschrift, sondern auf den gesamten Abschnitt mit dem Titel ECMAScript Modules in Web Workers verlinken möchte? Wenn der gesamte Text des Abschnitts prozentcodiert würde, wäre die resultierende URL unpraktisch lang.

Zum Glück gibt es eine bessere Möglichkeit. Anstatt den gesamten Text zu verwenden, kann ich einen bestimmten Teil des Texts mit der Syntax start,end einrahmen. Ich gebe am Anfang des Textfragments ein paar prozentcodierte Wörter und am Ende des Textfragments ein paar prozentcodierte Wörter an, die durch ein Komma getrennt sind.

Das sieht so aus:

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

Für start habe ich ECMAScript%20Modules%20in%20Web%20Workers, dann ein Komma, gefolgt von ES%20Modules%20in%20Web%20Workers. als end. Wenn ihr in einem unterstützten Browser wie Chrome auf den Link klickt, wird der gesamte Abschnitt hervorgehoben und in die Ansicht gescrollt:

Textfragment, das in die Ansicht gescrollt und hervorgehoben wurde.

Sie fragen sich vielleicht, warum ich start und end gewählt habe. Tatsächlich hätte die etwas kürzere URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules,Web%20Workers. mit nur zwei Wörtern auf jeder Seite auch funktioniert. Vergleichen Sie start und end mit den vorherigen Werten.

Wenn ich jetzt noch einen Schritt weitergehe und nur ein Wort für start und end verwende, wird es problematisch. Die URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript,Workers. ist jetzt noch kürzer, aber das hervorgehobene Textfragment ist nicht mehr das ursprünglich ausgewählte. Die Hervorhebung endet beim ersten Vorkommen des Wortes Workers.. Das ist zwar richtig, aber nicht das, was ich hervorheben wollte. Das Problem ist, dass der ausgewählte Abschnitt nicht eindeutig durch die aktuellen einwortigen Werte start und end identifiziert wird:

Nicht beabsichtigtes Textfragment wird angezeigt und hervorgehoben.

prefix- und -suffix

Eine Möglichkeit, einen eindeutigen Link zu erhalten, besteht darin, ausreichend lange Werte für start und end zu verwenden. In einigen Situationen ist dies jedoch nicht möglich. Warum habe ich eigentlich den Blogpost zum Release von Chrome 80 als Beispiel ausgewählt? Die Antwort lautet, dass in dieser Version Textfragmente eingeführt wurden:

Blogpost-Text: Text-URL-Fragmente. Nutzer oder Autoren können jetzt über ein Textfragment in einer URL auf einen bestimmten Teil einer Seite verlinken. Wenn die Seite geladen wird, hebt der Browser den Text hervor und scrollt zum Fragment. Mit der folgenden URL wird beispielsweise eine Wiki-Seite für „Katze“ geladen und zum Inhalt gescrollt, der im Parameter `text` aufgeführt ist.
Auszug aus dem Blogpost zur Ankündigung von Textfragmenten.

Im Screenshot oben kommt das Wort „text“ viermal vor. Das vierte Vorkommen ist in grüner Codeschrift geschrieben. Wenn ich auf dieses bestimmte Wort verlinken möchte, setze ich start auf text. Da das Wort „text“ nur ein Wort ist, kann es kein end geben. Was kann ich tun? Die URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=text stimmt mit dem ersten Vorkommen des Wortes „Text“ in der Überschrift überein:

Textfragment, das mit dem ersten Vorkommen von „Text“ übereinstimmt.

Zum Glück gibt es eine Lösung. In solchen Fällen kann ich einen prefix- und einen -suffix angeben. Das Wort vor dem grünen Code-Schriftart-„Text“ ist „the“ und das Wort danach ist „parameter“. Keines der anderen drei Vorkommen des Wortes „text“ hat dieselben umgebenden Wörter. Mit diesem Wissen kann ich die vorherige URL anpassen und prefix- und -suffix hinzufügen. Wie die anderen Parameter müssen auch sie prozentcodiert sein und können mehr als ein Wort enthalten. https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=the-,text,-parameter. Damit der Parser die prefix- und die -suffix eindeutig identifizieren kann, müssen sie von der start und der optionalen end durch einen Bindestrich - getrennt werden.

Textfragment, das mit dem ausgewählten Vorkommen von „text“ übereinstimmt.

Die vollständige Syntax

Die vollständige Syntax von Textfragmenten lautet wie folgt: (Eckige Klammern kennzeichnen einen optionalen Parameter.) Die Werte für alle Parameter müssen prozentcodiert sein. Das ist besonders wichtig für den Bindestrich -, das kaufmännische Und & und das Komma ,, damit sie nicht als Teil der Syntax der Textanweisung interpretiert werden.

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

prefix-, start, end und -suffix können jeweils nur Text innerhalb eines einzelnen Elements auf Blockebene abgleichen, aber vollständige start,end-Bereiche können sich über mehrere Blöcke erstrecken. Beispielsweise wird :~:text=The quick,lazy dog im folgenden Beispiel nicht gefunden, da der Anfangsstring „The quick“ nicht in einem einzelnen, ununterbrochenen Element auf Blockebene vorkommt:

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

In diesem Beispiel stimmt es jedoch überein:

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

Textfragment-URLs mit einer Browsererweiterung erstellen

Das manuelle Erstellen von Textfragment-URLs ist mühsam, insbesondere wenn sie eindeutig sein sollen. Wenn ihr das wirklich möchtet, findet ihr in der Spezifikation einige Tipps und die genauen Schritte zum Generieren von Textfragment-URLs. Wir bieten eine Open-Source-Browsererweiterung namens Link to Text Fragment an, mit der ihr einen Link zu einem beliebigen Text erstellen könnt. Dazu müsst ihr den Text auswählen und dann im Kontextmenü auf „Copy Link to Selected Text“ (Link zu ausgewähltem Text kopieren) klicken. Diese Erweiterung ist für die folgenden Browser verfügbar:

Link to Text Fragment -Browsererweiterung.

Mehrere Textfragmente in einer URL

In einer URL können mehrere Textfragmente enthalten sein. Die einzelnen Textfragmente müssen durch ein kaufmännisches Und-Zeichen (&) getrennt werden. Hier ein Beispiellink mit drei Textfragmenten: 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.

Drei Textfragmente in einer URL.

Element- und Textfragmente mischen

Herkömmliche Elementfragmente können mit Textfragmenten kombiniert werden. Es ist völlig in Ordnung, beide in derselben URL zu verwenden, z. B. um einen sinnvollen Fallback bereitzustellen, falls sich der ursprüngliche Text auf der Seite ändert und das Textfragment nicht mehr übereinstimmt. Die URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1:~:text=Give%20us%20feedback%20in%20our%20Product%20Forums., die auf den Abschnitt Feedback in unseren Produktforen geben enthält sowohl ein Elementfragment (HTML1) als auch ein Textfragment (text=Give%20us%20feedback%20in%20our%20Product%20Forums.):

Verknüpfung mit Element- und Textfragment.

Die Fragmentanweisung

Ein Element der Syntax habe ich noch nicht erklärt: die Fragmentanweisung :~:. Um Kompatibilitätsprobleme mit vorhandenen URL-Elementfragmenten wie oben gezeigt zu vermeiden, wird in der Spezifikation für Textfragmente die Fragmentanweisung eingeführt. Die Fragmentanweisung ist ein Teil des URL-Fragments, der durch die Codesequenz :~: begrenzt wird. Es ist für User-Agent-Anweisungen wie text= reserviert und wird beim Laden aus der URL entfernt, damit Autorenskripts nicht direkt damit interagieren können. Anweisungen für User-Agents werden auch als Anweisungen bezeichnet. In diesem konkreten Fall wird text= daher als Textanweisung bezeichnet.

Feature-Erkennung

Wenn Sie die Unterstützung prüfen möchten, testen Sie das Attribut fragmentDirective mit Lesezugriff für document. Die Fragment-Anweisung ist ein Mechanismus für URLs, um Anweisungen an den Browser und nicht an das Dokument zu senden. Dadurch soll eine direkte Interaktion mit dem Autorensript vermieden werden, damit zukünftige User-Agent-Anweisungen hinzugefügt werden können, ohne dass funktionsgefährdende Änderungen an vorhandenen Inhalten vorgenommen werden müssen. Ein mögliches Beispiel für solche zukünftigen Ergänzungen könnten Übersetzungshinweise sein.

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

Die Feature-Erkennung ist hauptsächlich für Fälle gedacht, in denen Links dynamisch generiert werden (z. B. von Suchmaschinen), um zu vermeiden, dass Links zu Textfragmenten für Browser bereitgestellt werden, die sie nicht unterstützen.

Textfragmente formatieren

Standardmäßig formatieren Browser Textfragmente genauso wie mark (normalerweise schwarz auf gelb, die CSS-Systemfarben für mark). Das User-Agent-Stylesheet enthält CSS, das so aussieht:

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

Wie Sie sehen, stellt der Browser einen Pseudoselektor ::target-text bereit, mit dem Sie die angewendete Hervorhebung anpassen können. Sie können Ihre Textfragmente beispielsweise als schwarzen Text auf rotem Hintergrund gestalten. Prüfen Sie wie immer den Farbkontrast, damit Ihr überschriebenes Styling keine Probleme mit der Barrierefreiheit verursacht und die Hervorhebung sich visuell vom restlichen Inhalt abhebt.

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

Polyfill

Die Funktion „Textfragmente“ kann bis zu einem gewissen Grad mit einem Polyfill versehen werden. Wir stellen ein Polyfill bereit, das intern von der Erweiterung für Browser verwendet wird, die keine integrierte Unterstützung für Textfragmente bieten, bei denen die Funktionalität in JavaScript implementiert ist.

Das Polyfill enthält eine Datei fragment-generation-utils.js, die Sie importieren und zum Generieren von Textfragment-Links verwenden können. Dies wird im Codebeispiel dargestellt:

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

Abrufen von Textfragmenten zu Analysezwecken

Viele Websites verwenden das Fragment für das Routing. Deshalb entfernen Browser Textfragmente, um diese Seiten nicht zu beschädigen. Es besteht ein anerkannter Bedarf, Textfragment-Links zu Seiten offenzulegen, z. B. für Analysezwecke. Die vorgeschlagene Lösung ist jedoch noch nicht implementiert. Als vorübergehende Lösung können Sie den folgenden Code verwenden, um Informationen zu extrahieren.

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

Sicherheit

Textfragment-Anweisungen werden nur bei vollständigen Navigationen (nicht auf derselben Seite) aufgerufen, die das Ergebnis einer Nutzeraktivierung sind. Außerdem müssen Navigationen, die von einem anderen Ursprung als dem Ziel stammen, in einem noopener-Kontext erfolgen, damit die Zielseite ausreichend isoliert ist. Anweisungen für Textfragmente werden nur auf den Hauptframe angewendet. Das bedeutet, dass Text nicht in iFrames gesucht wird und die iFrame-Navigation kein Textfragment aufruft.

Datenschutz

Es ist wichtig, dass bei Implementierungen der Textfragmente-Spezifikation nicht preisgegeben wird, ob ein Textfragment auf einer Seite gefunden wurde oder nicht. Während Elementfragmente vollständig unter der Kontrolle des ursprünglichen Seitenautors stehen, können Textfragmente von jedem erstellt werden. In meinem Beispiel gab es keine Möglichkeit, auf die Überschrift ECMAScript Modules in Web Workers zu verlinken, da das <h1> kein id hatte. Aber jeder, auch ich, konnte einfach auf eine beliebige Stelle verlinken, indem er das Textfragment sorgfältig erstellte.

Angenommen, ich betreibe ein bösartiges Werbenetzwerk evil-ads.example.com. Stellen Sie sich außerdem vor, dass ich in einem meiner Anzeigen-iFrames dynamisch einen ausgeblendeten ursprungsübergreifenden iFrame für dating.example.com mit einer Textfragment-URL dating.example.com#:~:text=Log%20Out erstellt habe, sobald der Nutzer mit der Anzeige interagiert. Wenn der Text „Abmelden“ gefunden wird, weiß ich, dass das Opfer bei dating.example.com angemeldet ist. Das könnte ich für die Erstellung eines Nutzerprofils verwenden. Da bei einer einfachen Textfragmente-Implementierung ein erfolgreicher Treffer zu einem Fokuswechsel führen könnte, könnte ich auf evil-ads.example.com auf das blur-Ereignis warten und so feststellen, wann ein Treffer aufgetreten ist. In Chrome haben wir Textfragmente so implementiert, dass ein solches Szenario nicht eintreten kann.

Ein weiterer Angriff könnte darin bestehen, den Netzwerkverkehr basierend auf der Scrollposition auszunutzen. Angenommen, ich hätte Zugriff auf die Netzwerkverkehrsprotokolle meines Opfers, z. B. als Administrator eines Unternehmensintranets. Stellen Sie sich nun vor, es gäbe ein langes HR-Dokument mit dem Titel Was tun, wenn Sie unter… leiden, gefolgt von einer Liste von Erkrankungen wie Burnout, Angstzuständen usw. Ich könnte neben jedem Element auf der Liste ein Tracking-Pixel platzieren. Wenn ich dann feststelle, dass das Laden des Dokuments zeitlich mit dem Laden des Tracking-Pixels neben dem Element Burnout zusammenfällt, kann ich als Intranet-Administrator feststellen, dass ein Mitarbeiter auf einen Textfragment-Link mit :~:text=burn%20out geklickt hat, von dem der Mitarbeiter möglicherweise angenommen hat, dass er vertraulich und für niemanden sichtbar ist. Da dieses Beispiel etwas konstruiert ist und für die Ausnutzung sehr spezifische Voraussetzungen erfüllt sein müssen, hat das Chrome-Sicherheitsteam das Risiko der Implementierung von Scrollen bei der Navigation als überschaubar eingestuft. Andere User-Agents zeigen stattdessen möglicherweise ein UI-Element für manuelles Scrollen an.

Für Websites, die sich deaktivieren möchten, unterstützt Chromium einen Headerwert für die Dokumentrichtlinie, den sie senden können, damit User-Agents keine Textfragment-URLs verarbeiten.

Document-Policy: force-load-at-top

Textfragmente deaktivieren

Am einfachsten lässt sich die Funktion mit einer Erweiterung deaktivieren, die HTTP-Antwortheader einfügen kann, z. B. ModHeader (kein Google-Produkt). Damit lässt sich ein Antwortheader (kein Anfrageheader) so einfügen:

Document-Policy: force-load-at-top

Eine weitere, komplexere Möglichkeit, die Funktion zu deaktivieren, ist die Verwendung der Enterprise-Einstellung ScrollToTextFragmentEnabled. Fügen Sie dazu unter macOS den folgenden Befehl in das Terminal ein.

defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false

Folgen Sie unter Windows der Dokumentation auf der Supportwebsite der Google Chrome Enterprise-Hilfe.

Bei einigen Suchanfragen liefert die Suchmaschine Google eine schnelle Antwort oder Zusammenfassung mit einem Content-Snippet von einer relevanten Website. Diese hervorgehobenen Snippets werden am wahrscheinlichsten angezeigt, wenn eine Suchanfrage in Form einer Frage gestellt wird. Durch Klicken auf ein hervorgehobenes Snippet gelangt der Nutzer direkt zum Text des hervorgehobenen Snippets auf der Quellwebseite. Das funktioniert dank automatisch erstellter Textfragment-URLs.

Google-Suchergebnisseite mit einem hervorgehobenen Snippet. In der Statusleiste wird die Textfragmente-URL angezeigt.
Nach dem Klicken wird der entsprechende Abschnitt der Seite angezeigt.

Fazit

Textfragmente-URLs sind eine leistungsstarke Funktion, um auf beliebigen Text auf Webseiten zu verlinken. Die wissenschaftliche Gemeinschaft kann damit hochpräzise Zitations- oder Referenzlinks bereitstellen. Suchmaschinen können damit Deeplinks zu Textergebnissen auf Seiten erstellen. Websites sozialer Netzwerke können damit Nutzern ermöglichen, bestimmte Passagen einer Webseite zu teilen, anstatt unzugängliche Screenshots zu verwenden. Ich hoffe, dass Sie Textfragment-URLs verwenden und sie genauso nützlich finden wie ich. Installieren Sie die Browsererweiterung Link to Text Fragment.

Danksagungen

Textfragmente wurden von Nick Burris und David Bokan implementiert und spezifiziert, mit Beiträgen von Grant Wang. Vielen Dank an Joe Medley für die gründliche Überprüfung dieses Dokuments.