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

Mit Text-Snippets können Sie ein Text-Snippet im URL-Fragment angeben. Wenn der Browser zu einer URL mit einem solchen Textfragment navigiert, kann sie betont und/oder auf den Nutzer aufmerksam gemacht werden.

Fragment-IDs

Chrome 80 war eine große Veröffentlichung. Sie enthielt eine Reihe von mit Spannung erwarteten Funktionen wie ECMAScript-Module in Web Workers, Null Coalescing und optionale verkettung. Die Veröffentlichung wurde wie gewohnt in einem Blogpost im Chromium-Blog angekündigt. Im Screenshot unten sehen Sie einen Auszug aus dem Blogpost.

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

Sie fragen sich wahrscheinlich, was all 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';
});

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

Entwicklertools mit der id eines Elements

Wenn ich diese URL mit dem URL()-Konstruktor von JavaScript parse, sind die verschiedenen Komponenten sichtbar. 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 dieser Abschnitt der Seite vom Autor des Blogposts verlinkt werden sollte.

Wie gehe ich vor, wenn ich auf etwas ohne id verlinken möchte? Angenommen, ich möchte einen Link zur Überschrift ECMAScript-Module in Webworkern erstellen. Wie Sie im Screenshot unten sehen, hat die betreffende <h1> kein id-Attribut. Ich kann also keine Verknüpfung zu dieser Überschrift herstellen. Dieses Problem wird durch Textfragmente gelöst.

Entwicklertools mit einer Überschrift ohne id.

Textfragmente

Mit dem Vorschlag Text Fragments wird die Angabe eines Text-Snippets im URL-Hash unterstützt. Wenn eine URL mit einem solchen Textfragment aufgerufen wird, kann der User-Agent das Textfragment hervorheben und/oder die Aufmerksamkeit des Nutzers darauf lenken.

Browserkompatibilität

Unterstützte Browser

  • Chrome: 89.
  • Edge: 89.
  • Firefox: 131.
  • Safari-Technologievorschau: unterstützt.

Quelle

Aus Sicherheitsgründen müssen Links für die Funktion in einem noopener-Kontext geöffnet werden. Achte daher darauf, rel="noopener" in dein <a>-Anker-Markup aufzunehmen oder noopener deiner Window.open()-Liste mit Fensterfunktionsfunktionen hinzuzufügen.

start

In der einfachsten Form lautet die Syntax von Textfragmenten so: Das Hash-Symbol # gefolgt von :~:text= und schließlich start für den prozentcodierten Text, zu dem ich eine Verknüpfung herstellen möchte.

#:~:text=start

Wenn ich beispielsweise einen Link zur Überschrift ECMAScript-Module in Web Workers im Blogpost zu den Funktionen in Chrome 80 setzen möchte, lautet die URL in diesem Fall:

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 Sie in einem unterstützten Browser wie Chrome auf den Link klicken, wird das Textfragment hervorgehoben und in den Blick gescrollt:

Textfragment, das sichtbar gescrollt und hervorgehoben wird

start und end

Was ist, wenn ich auf den gesamten Abschnitt mit dem Titel ECMAScript-Module in Web Workers und nicht nur auf die Überschrift verlinken möchte? Wenn der gesamte Text des Abschnitts mit Prozent codiert wird, wäre die resultierende URL unpraktisch lang.

Zum Glück gibt es eine bessere Lösung. Anstelle des gesamten Textes kann ich den gewünschten Text mit der Syntax start,end umrahmen. Daher gebe ich einige Prozentcodierte Wörter am Anfang des gewünschten Textes und einige Prozentcodierte Wörter am Ende des gewünschten Textes an, getrennt durch ein Komma ,.

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 du in einem unterstützten Browser wie Chrome klickst, wird der gesamte Bereich hervorgehoben und in den Blick gescrollt:

Textfragment, das sichtbar gescrollt und hervorgehoben wird

Sie fragen sich jetzt vielleicht, warum ich start und end ausgewählt habe. 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 hätte auch funktioniert. Vergleichen Sie start und end mit den vorherigen Werten.

Wenn ich einen Schritt weitergehe und jetzt nur ein Wort für start und end verwende, kann ich das Problem nachvollziehen. 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 markierte Textfragment ist nicht mehr das ursprünglich gewünschte. Die Markierung endet beim ersten Vorkommen des Wortes Workers.. Das ist richtig, aber nicht das, was ich hervorheben wollte. Das Problem ist, dass der gewünschte Abschnitt durch die aktuellen einsilbigen Werte start und end nicht eindeutig identifiziert wird:

Nicht beabsichtigtes Textfragment wird sichtbar und hervorgehoben.

prefix- und -suffix

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

Text des Blogposts: 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 ist, hebt der Browser den Text hervor und scrollt das Fragment in den Blick. 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 ist das Wort „Text“ viermal zu sehen. Das vierte Vorkommen ist in einer grünen Codeschrift geschrieben. Wenn ich einen Link zu diesem bestimmten Wort erstellen möchte, würde ich start auf text setzen. Da das Wort „Text“ nur ein Wort ist, kann es keine 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“ überein, das bereits in der Überschrift enthalten ist:

Textfragmentabgleich beim ersten Vorkommen von „Text“.

Glücklicherweise gibt es eine Lösung. In solchen Fällen kann ich eine prefix​- und eine -suffix angeben. Das Wort vor der grünen Codeschrift „text“ ist „the“ und das Wort danach „parameter“. Bei keinem der drei anderen Vorkommen des Wortes „Text“ sind die umgebenden Wörter identisch. Mit diesem Wissen kann ich die vorherige URL anpassen und prefix- und -suffix hinzufügen. Wie die anderen Parameter müssen auch diese mit dem Prozentzeichen codiert sein und können aus mehreren Wörtern bestehen. https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=the-,text,-parameter. Damit der Parser prefix- und -suffix eindeutig identifizieren kann, müssen sie durch einen Bindestrich - von start und dem optionalen end getrennt werden.

Textfragmentabgleich am gewünschten Vorkommen von „Text“.

Vollständige Syntax

Die vollständige Syntax von Textfragmenten finden Sie unten. (Eckige Klammern kennzeichnen einen optionalen Parameter.) Die Werte für alle Parameter müssen prozentcodiert werden. Das ist besonders wichtig für den Bindestrich -, das Ampezeichen & und das Komma ,, damit sie nicht als Teil der Syntax der Textrichtlinie interpretiert werden.

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

prefix-, start, end und -suffix werden nur mit Text innerhalb eines einzelnen Elements auf Blockebene abgeglichen. Vollständige start,end-Bereiche können jedoch mehrere Blöcke umfassen. Im folgenden Beispiel wird :~:text=The quick,lazy dog beispielsweise nicht gefunden, da der Anfangsstring „The quick“ nicht in einem einzelnen, ununterbrochenen Element auf Blockebene erscheint:

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

In diesem Beispiel stimmt sie 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 es darum geht, sicherzustellen, dass sie eindeutig sind. In der Spezifikation finden Sie einige Tipps und eine genaue Anleitung zum Erstellen von Textfragment-URLs. Wir stellen eine Open-Source-Browsererweiterung namens Link to Text Fragment (Link zum Textfragment) bereit, mit der Sie einen Link zu beliebigem Text erstellen können. Wählen Sie dazu den Text aus und klicken Sie dann im Kontextmenü auf „Link zum ausgewählten Text kopieren“. Diese Erweiterung ist für die folgenden Browser verfügbar:

Link zum Textfragment in der Browsererweiterung.

Mehrere Textfragmente in einer URL

In einer URL können mehrere Textfragmente vorkommen. Die einzelnen Textfragmente müssen durch ein &-Zeichen getrennt werden. Hier ein Beispiel für einen Link 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 kombinieren

Elementfragmente können mit Textfragmenten kombiniert werden. Es ist völlig in Ordnung, beides in derselben URL zu haben, z. B. um einen sinnvollen Fallback zu bieten, falls sich der ursprüngliche Text auf der Seite ändert, sodass 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 zum Abschnitt Feedback in unseren Produktforen führt, enthält sowohl ein Elementfragment (HTML1) als auch ein Textfragment (text=Give%20us%20feedback%20in%20our%20Product%20Forums.):

Verknüpfung mit Elementfragment und Textfragment

Die Fragmentanweisung

Es gibt ein Element der Syntax, das ich noch nicht erklärt habe: die Fragment-Anweisung :~:. Um Kompatibilitätsprobleme mit vorhandenen URL-Element-Fragmenten wie oben gezeigt zu vermeiden, wird in der Spezifikation für Textfragmente die Fragmentdirektive eingeführt. Die Fragmentdirektive ist ein Teil des URL-Fragments, der durch die Codesequenz :~: abgegrenzt ist. Sie ist für User-Agent-Anweisungen wie text= reserviert und wird beim Laden aus der URL entfernt, sodass Autorenskripts nicht direkt mit ihr interagieren können. User-Agent-Anweisungen werden auch als Anweisungen bezeichnet. Im konkreten Fall wird text= daher als Textdirektive bezeichnet.

Funktionserkennung

Um die Unterstützung zu prüfen, testen Sie die schreibgeschützte Property fragmentDirective auf document. Die Fragmentdirektive ist ein Mechanismus für URLs, mit dem Anweisungen an den Browser und nicht an das Dokument angegeben werden. So soll eine direkte Interaktion mit dem Autor-Script vermieden werden, damit zukünftige Anweisungen für User-Agents hinzugefügt werden können, ohne dass bestehende Inhalte durch bahnbrechende Änderungen beeinträchtigt werden. Ein mögliches Beispiel für solche zukünftigen Ergänzungen sind Übersetzungshinweise.

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

Die Funktion ist hauptsächlich für Fälle gedacht, in denen Links dynamisch generiert werden (z. B. von Suchmaschinen), um zu vermeiden, dass Textfragmente in Browsern präsentiert werden, die sie nicht unterstützen.

Stile für Textfragmente festlegen

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

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

Wie Sie sehen, zeigt der Browser eine Pseudoauswahl ::target-text an, mit der Sie die angewendete Hervorhebung anpassen können. Sie könnten Ihre Textfragmente zum Beispiel als schwarzer Text auf rotem Hintergrund gestalten. Wie immer sollten Sie den Farbkontrast prüfen, damit das Überschreibungs-Styling keine Probleme mit der Barrierefreiheit verursacht. Achten Sie außerdem darauf, dass sich die Hervorhebung optisch vom Rest des Inhalts abhebt.

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

Polyfill-Fähigkeit

Die Funktion „Textfragmente“ kann zu einem gewissen Grad mit Polyfüllung gefüllt werden. Für Browser, die keine integrierte Unterstützung für Textfragmente bieten, wenn die Funktionalität in JavaScript implementiert ist, stellen wir einen Polyfill zur Verfügung, der intern von der Erweiterung verwendet wird.

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

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

Textfragmente zu Analysezwecken abrufen

Viele Websites verwenden das Fragment für das Routing. Aus diesem Grund entfernen Browser Textfragmente, damit diese Seiten nicht beschädigt werden. Es besteht ein anerkannter Bedarf, Textfragmente mit Links zu Seiten zu versehen, z. B. zu Analysezwecken. Die vorgeschlagene Lösung ist jedoch noch nicht implementiert. Als vorübergehende Lösung können Sie die gewünschten Informationen mit dem Code unten extrahieren.

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

Sicherheit

Textfragment-Direktiven werden nur bei vollständigen Navigationen (nicht auf derselben Seite) aufgerufen, die auf eine Nutzeraktion zurückzuführen sind. Außerdem muss die Navigation von einem anderen Ursprung als dem Ziel in einem noopener-Kontext erfolgen, sodass die Zielseite hinreichend isoliert ist. Direktiven 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 der Implementierung der Spezifikation für Textfragmente nicht verraten wird, ob ein Textfragment auf einer Seite gefunden wurde oder nicht. Während Elementfragmente vollständig vom ursprünglichen Seitenautor verwaltet werden, können Textfragmente von jedem erstellt werden. Denken Sie daran, dass in meinem Beispiel oben kein Link zur Überschrift ECMAScript-Module in Webworkern möglich war, da die <h1> kein id hatte, aber dass jeder, einschließlich mir, durch sorgfältiges Erstellen des Textfragments einfach einen Link zu einer beliebigen Stelle erstellen konnte?

Angenommen, ich betreibe ein bösartiges Werbenetzwerk evil-ads.example.com. Angenommen, in einem meiner Anzeigen-iframes habe ich dynamisch einen versteckten, plattformübergreifenden iframe zu dating.example.com mit einer Textfragment-URLdating.example.com#:~:text=Log%20Out erstellt, sobald der Nutzer mit der Anzeige interagiert. Wenn der Text „Abmelden“ gefunden wird, weiß ich, dass das Opfer derzeit in dating.example.com angemeldet ist. Das kann ich für das Nutzerprofil verwenden. Da eine naive Implementierung von Textfragmenten möglicherweise festlegt, dass eine erfolgreiche Übereinstimmung einen Fokuswechsel auslösen soll, könnte ich auf evil-ads.example.com auf das Ereignis blur warten und so erkennen, wann eine Übereinstimmung stattgefunden hat. In Chrome sind Textfragmente so implementiert, dass das oben beschriebene Szenario nicht möglich ist.

Ein weiterer Angriff könnte darin bestehen, den Netzwerkverkehr basierend auf der Scroll-Position auszunutzen. Angenommen, ich habe Zugriff auf die Netzwerkverkehrsprotokolle meines Opfers, z. B. als Administrator eines Unternehmens-Intranets. Stellen Sie sich nun vor, es gäbe ein langes Personaldokument mit dem Titel Was tun, wenn Sie an… leiden? und eine Liste mit Erkrankungen wie Burn-out oder Angst. Ich könnte neben jedem Element auf der Liste ein Tracking-Pixel platzieren. Wenn ich dann feststelle, dass das Laden des Dokuments zeitlich zusammen mit dem Laden des Tracking-Pixels neben dem ausgebrannten Element stattfindet, kann ich als Intranet-Administrator feststellen, dass ein Mitarbeiter auf einen Textfragment-Link mit :~:text=burn%20out geklickt hat, von dem er möglicherweise angenommen hat, dass er vertraulich und für niemanden sichtbar war. Da dieses Beispiel von vornherein 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 Navigation“ als beherrschbar eingestuft. Andere User-Agents können stattdessen ein UI-Element für das manuelle Scrollen anzeigen.

Für Websites, die die Funktion 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

Die Funktion lässt sich am einfachsten mit einer Erweiterung deaktivieren, die HTTP-Antwortheader einschleusen kann, z. B. ModHeader (kein Google-Produkt). So fügen Sie einen Antwort- (nicht Anfrage-)Header ein:

Document-Policy: force-load-at-top

Eine weitere, etwas aufwändigere Möglichkeit, die Funktion zu deaktivieren, ist die Verwendung der Enterprise-Einstellung ScrollToTextFragmentEnabled. Unter macOS fügen Sie dazu den folgenden Befehl in das Terminal ein.

defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false

Folgen Sie unter Windows der Dokumentation auf der Supportwebsite für Google Chrome Enterprise.

Bei einigen Suchanfragen liefert die Google Suche eine kurze Antwort oder Zusammenfassung mit einem Content-Snippet von einer relevanten Website. Solche Hervorgehobene Snippets erscheinen insbesondere bei Suchanfragen, die als Frage formuliert sind. Durch Klicken auf ein hervorgehobenes Snippet gelangt der Nutzer direkt zum Text des hervorgehobenen Snippets auf der Quellwebseite. Das funktioniert dank automatisch erstellter URLs für Textfragmente.

Google-Suchmaschinenergebnisseite mit einem angezeigten Snippet. In der Statusleiste wird die URL der Textfragmente angezeigt.
Nach dem Klick wird der relevante Bereich der Seite sichtbar gescrollt.

Fazit

Die URL für Textfragmente ist eine leistungsstarke Funktion, mit der Sie auf beliebigen Text auf Webseiten verlinken können. Die wissenschaftliche Gemeinschaft kann damit sehr genaue Quellenangaben oder Referenzlinks bereitstellen. Suchmaschinen können damit Deeplinks zu Textergebnissen auf Seiten erstellen. Nutzer von sozialen Netzwerken können damit bestimmte Passagen einer Webseite teilen, anstatt nicht barrierefreie Screenshots. Ich hoffe, dass Sie Textfragment-URLs verwenden und sie genauso nützlich finden wie ich. Installieren Sie dazu die Browsererweiterung Link zum Textfragment.

Danksagungen

Textfragmente wurden von Nick Burris und David Bokan mit Beiträgen von Grant Wang implementiert und spezifiziert. Vielen Dank an Joe Medley für die sorgfältige Überprüfung dieses Artikels. Hero-Image von Greg Rakozy auf Unsplash.