Fragmenty tekstu pogrubione linki, do których nikt wcześniej nie linkował

Fragmenty tekstu umożliwiają określenie fragmentu tekstu we fragmencie adresu URL. Gdy użytkownik przechodzi do adresu URL z takim fragmentem tekstu, przeglądarka może go wyróżnić i zwrócić na niego uwagę użytkownika.

Identyfikatory fragmentów

Chrome 80 to była ważna wersja. Zawierała ona wiele bardzo oczekiwanych funkcji, takich jak moduły ECMAScript w skryptach Web Worker, operator nullish coalescing, optional chaining i inne. Wydanie zostało, jak zwykle, ogłoszone w poście na blogu na blogu Chromium. Poniżej znajdziesz fragment posta na blogu.

Post na blogu Chromium z czerwonymi polami wokół elementów z atrybutem id.

Zastanawiasz się pewnie, co oznaczają te wszystkie czerwone pola. Są one wynikiem uruchomienia w Narzędziach deweloperskich tego fragmentu kodu. Wyróżnia wszystkie elementy, które mają atrybut id.

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

Mogę umieścić precyzyjny link do dowolnego elementu wyróżnionego czerwonym polem dzięki identyfikatorowi fragmentu, którego następnie używam w haszu adresu URL strony. Jeśli chcę utworzyć precyzyjny link do pola Prześlij nam opinię na forach pomocy w panelu bocznym, mogę to zrobić, ręcznie tworząc adres URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1. Jak widać w panelu Elementy w Narzędziach dla programistów, dany element ma atrybut id o wartości HTML1.

Narzędzia deweloperskie pokazujące id elementu.

Jeśli przeanalizuję ten adres URL za pomocą konstruktora URL() w języku JavaScript, zostaną ujawnione różne komponenty. Zwróć uwagę na właściwość hash z wartością #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: ""
}
*/

Fakt, że musiałem otworzyć Narzędzia dla programistów, aby znaleźć id elementu, mówi sam za siebie, jeśli chodzi o prawdopodobieństwo, że autor posta na blogu chciał utworzyć link do tej konkretnej sekcji strony.

Co zrobić, jeśli chcę utworzyć link do czegoś, co nie ma id? Załóżmy, że chcę utworzyć link do nagłówka Moduły ECMAScript w Web Workers. Jak widać na zrzucie ekranu poniżej, dany element <h1> nie ma atrybutu id, co oznacza, że nie mogę utworzyć linku do tego nagłówka. Ten problem rozwiązują fragmenty tekstu.

Narzędzia deweloperskie pokazujące nagłówek bez elementu id.

Fragmenty tekstu

Propozycja Text Fragments dodaje obsługę określania fragmentu tekstu w haszu adresu URL. Podczas przechodzenia do adresu URL z takim fragmentem tekstu klient użytkownika może go wyróżnić i zwrócić na niego uwagę użytkownika.

Zgodność z przeglądarką

Browser Support

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

Source

Ze względów bezpieczeństwa ta funkcja wymaga otwierania linków w kontekście noopener. Dlatego pamiętaj, aby uwzględnić rel="noopener" w znacznikach kotwicy <a> lub dodać noopener do listy funkcji okna Window.open().

start

W najprostszej postaci składnia fragmentów tekstu wygląda tak: symbol krzyżyka #, po którym następuje :~:text=, a na końcu start, który reprezentuje zakodowany procentowo tekst, do którego chcę utworzyć link.

#:~:text=start

Jeśli na przykład chcę utworzyć link do nagłówka Moduły ECMAScript w Web Workers w poście na blogu, w którym ogłoszono funkcje w Chrome 80, adres URL będzie wyglądał tak:

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

Fragment tekstu jest wyróżniony w ten sposób. Jeśli klikniesz link w obsługiwanej przeglądarce, np. Chrome, fragment tekstu zostanie podświetlony i przewinięty do widoku:

Fragment tekstu przewinięty do widocznego obszaru i wyróżniony.

start i end

Co zrobić, jeśli chcę utworzyć link do całej sekcji zatytułowanej Moduły ECMAScript w instancjach Web Worker, a nie tylko do jej nagłówka? Kodowanie procentowe całego tekstu sekcji spowodowałoby, że wynikowy adres URL byłby zbyt długi.

Na szczęście istnieje lepsze rozwiązanie. Zamiast całego tekstu mogę ująć w ramkę jego konkretną część, używając składni start,end. Na początku fragmentu tekstu podaję kilka słów zakodowanych procentowo, a na końcu fragmentu tekstu – kilka słów zakodowanych procentowo, rozdzielonych przecinkiem.

Wygląda to tak:

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

W przypadku start mam ECMAScript%20Modules%20in%20Web%20Workers, potem przecinek, a następnie ES%20Modules%20in%20Web%20Workers. jako end. Gdy klikniesz link w obsługiwanej przeglądarce, np. Chrome, cała sekcja zostanie podświetlona i przewinięta do widoku:

Fragment tekstu przewinięty do widocznego obszaru i wyróżniony.

Możesz się zastanawiać, dlaczego wybrałem start i end. Właściwie nieco krótszy adres URL: https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules,Web%20Workers. z tylko 2 słowami po każdej stronie też by zadziałał. Porównaj start i end z poprzednimi wartościami.

Jeśli pójdę o krok dalej i będę używać tylko jednego słowa dla start i end, będę mieć problem. Adres URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript,Workers. jest teraz jeszcze krótszy, ale wyróżniony fragment tekstu nie jest już pierwotnie wybranym fragmentem. Zaznaczenie kończy się na pierwszym wystąpieniu słowa Workers., co jest prawidłowe, ale nie jest tym, co chciałem zaznaczyć. Problem polega na tym, że wybrana sekcja nie jest jednoznacznie identyfikowana przez bieżące jednowyrazowe wartości start i end:

Przewinięcie do niezamierzonego fragmentu tekstu i jego wyróżnienie.

prefix- i -suffix

Jednym ze sposobów na uzyskanie unikalnego linku jest użycie wystarczająco długich wartości parametrów start i end. W niektórych sytuacjach nie jest to jednak możliwe. Dlaczego jako przykład wybrałem post na blogu o Chrome 80? Odpowiedź brzmi: w tej wersji wprowadziliśmy fragmenty tekstu:

Tekst posta na blogu: fragmenty adresu URL tekstu. Użytkownicy lub autorzy mogą teraz tworzyć linki do konkretnej części strony, używając fragmentu tekstu podanego w adresie URL. Po załadowaniu strony przeglądarka podświetla tekst i przewija fragment do widoku. Na przykład adres URL poniżej wczytuje stronę wiki o kotach i przewija do treści wymienionych w parametrze `text`.
Fragment posta na blogu z ogłoszeniem o funkcji Text Fragments.

Zwróć uwagę, że na zrzucie ekranu powyżej słowo „text” pojawia się 4 razy. Czwarty przypadek jest zapisany zieloną czcionką kodu. Jeśli chcę utworzyć link do tego konkretnego słowa, ustawiam start na text. Słowo „tekst” to tylko jedno słowo, więc nie może być end. Co teraz? Adres URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=text pasuje do pierwszego wystąpienia słowa „Text” w nagłówku:

Fragment tekstu dopasowany do pierwszego wystąpienia słowa „Tekst”.

Na szczęście jest na to rozwiązanie. W takich przypadkach mogę określić prefix- i -suffix. Słowo przed zielonym kodem „text” to „the”, a słowo po nim to „parameter”. Żadne z pozostałych 3 wystąpień słowa „tekst” nie ma takich samych słów wokół siebie. Mając tę wiedzę, mogę zmodyfikować poprzedni adres URL i dodać prefix- oraz -suffix. Podobnie jak inne parametry, muszą być zakodowane procentowo i mogą zawierać więcej niż 1 słowo. https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=the-,text,-parameter. Aby parser mógł jednoznacznie zidentyfikować prefix- i -suffix, muszą one być oddzielone od start i opcjonalnego end kreską -.

Fragment tekstu pasujący do wybranego wystąpienia „tekstu”.

Pełna składnia

Poniżej znajdziesz pełną składnię fragmentów tekstu. (Nawiasy kwadratowe oznaczają parametr opcjonalny). Wartości wszystkich parametrów muszą być zakodowane procentowo. Jest to szczególnie ważne w przypadku łącznika -, ampersandu & i przecinka ,, aby nie były one interpretowane jako część składni dyrektywy tekstowej.

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

Każdy z elementów prefix-, start, end i -suffix może pasować tylko do tekstu w obrębie jednego elementu blokowego, ale pełne zakresy start,end mogą obejmować wiele bloków. Na przykład w poniższym przypadku nie uda się dopasować ciągu :~:text=The quick,lazy dog, ponieważ ciąg początkowy „The quick” nie pojawia się w jednym nieprzerwanym elemencie na poziomie bloku:

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

Pasuje jednak w tym przykładzie:

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

Tworzenie adresów URL z fragmentem tekstu za pomocą rozszerzenia przeglądarki

Ręczne tworzenie adresów URL fragmentów tekstu jest żmudne, zwłaszcza jeśli chodzi o upewnienie się, że są one unikalne. Jeśli naprawdę chcesz to zrobić, w specyfikacji znajdziesz kilka wskazówek i dokładne kroki generowania adresów URL fragmentów tekstu. Udostępniamy rozszerzenie przeglądarki typu open source o nazwie Link to Text Fragment, które umożliwia utworzenie linku do dowolnego tekstu przez zaznaczenie go, a następnie kliknięcie „Copy Link to Selected Text” (Kopiuj link do zaznaczonego tekstu) w menu kontekstowym. To rozszerzenie jest dostępne w tych przeglądarkach:

Link to Text Fragment .

Kilka fragmentów tekstu w jednym adresie URL

Pamiętaj, że w jednym adresie URL może występować kilka fragmentów tekstu. Poszczególne fragmenty tekstu muszą być oddzielone znakiem ampersand &. Oto przykład linku z 3 fragmentami tekstu: 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.

3 fragmenty tekstu w jednym adresie URL.

Mieszanie fragmentów elementów i tekstu

Tradycyjne fragmenty elementów można łączyć z fragmentami tekstu. Możesz mieć oba te elementy w tym samym adresie URL, np. aby zapewnić odpowiedni adres zastępczy na wypadek, gdyby oryginalny tekst na stronie uległ zmianie i fragment tekstu już nie pasował. Adres URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1:~:text=Give%20us%20feedback%20in%20our%20Product%20Forums. prowadzący do sekcji Prześlij opinię na forach produktów zawiera zarówno fragment elementu (HTML1), jak i fragment tekstu (text=Give%20us%20feedback%20in%20our%20Product%20Forums.):

Tworzenie linków z użyciem fragmentu elementu i fragmentu tekstu

Dyrektywa fragmentu

Jest jeszcze jeden element składni, którego nie wyjaśniliśmy: dyrektywa fragmentu :~:. Aby uniknąć problemów ze zgodnością z istniejącymi fragmentami elementów adresu URL, jak pokazano na przykładzie, specyfikacja fragmentów tekstu wprowadza dyrektywę fragmentu. Dyrektywa fragmentu to część fragmentu adresu URL rozdzielona sekwencją kodu :~:. Jest on zarezerwowany dla instrukcji klienta użytkownika, takich jak text=, i jest usuwany z adresu URL podczas wczytywania, aby skrypty autora nie mogły bezpośrednio z nim współdziałać. Instrukcje dla klienta użytkownika są też nazywane dyrektywami. W tym konkretnym przypadku text= jest nazywany dyrektywą tekstową.

Wykrywanie funkcji

Aby wykryć obsługę, sprawdź właściwość fragmentDirective tylko do odczytu w document. Dyrektywa fragmentu to mechanizm adresów URL, który umożliwia określanie instrukcji skierowanych do przeglądarki, a nie do dokumentu. Ma to na celu uniknięcie bezpośredniej interakcji ze skryptem autora, dzięki czemu można dodawać przyszłe instrukcje klienta użytkownika bez obawy o wprowadzenie zmian powodujących niezgodność w istniejących treściach. Jednym z potencjalnych przykładów takich przyszłych dodatków mogą być wskazówki dotyczące tłumaczenia.

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

Wykrywanie funkcji jest przeznaczone głównie do przypadków, w których linki są generowane dynamicznie (np. przez wyszukiwarki), aby uniknąć wyświetlania linków do fragmentów tekstu w przeglądarkach, które ich nie obsługują.

Stylizowanie fragmentów tekstu

Domyślnie przeglądarki stylizują fragmenty tekstu tak samo jak mark (zwykle czarny na żółtym, kolory systemowe CSS dla mark). Arkusz stylów klienta użytkownika zawiera kod CSS, który wygląda tak:

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

Jak widać, przeglądarka udostępnia pseudoselektor ::target-text, którego można użyć do dostosowania zastosowanego wyróżnienia. Możesz na przykład zaprojektować fragmenty tekstu tak, aby były czarne na czerwonym tle. Jak zawsze pamiętaj, aby sprawdzić kontrast kolorów, aby zastąpienie stylu nie powodowało problemów z dostępnością, i upewnij się, że wyróżnienie faktycznie odróżnia się wizualnie od reszty treści.

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

Kod polyfill

Funkcję Text Fragments można w pewnym stopniu obsłużyć kodem polyfill. Udostępniamy polyfill, który jest używany wewnętrznie przez rozszerzenie w przypadku przeglądarek, które nie obsługują wbudowanych fragmentów tekstu, gdy funkcja jest zaimplementowana w języku JavaScript.

Polyfill zawiera plik fragment-generation-utils.js, który możesz zaimportować i użyć do generowania linków do fragmentów tekstu. Jest to opisane w przykładowym kodzie:

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

Uzyskiwanie fragmentów tekstu do celów analitycznych

Wiele witryn używa fragmentu do routingu, dlatego przeglądarki usuwają fragmenty tekstu, aby nie uszkodzić tych stron. Istnieje uznana potrzeba udostępniania linków do fragmentów tekstu na stronach, na przykład do celów analitycznych, ale proponowane rozwiązanie nie zostało jeszcze wdrożone. Tymczasowo możesz użyć tego kodu, aby wyodrębnić informacje.

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

Bezpieczeństwo

Dyrektywy fragmentów tekstu są wywoływane tylko w przypadku pełnych nawigacji (nie na tej samej stronie), które są wynikiem aktywacji użytkownika. Dodatkowo nawigacja rozpoczynająca się w innym źródle niż miejsce docelowe wymaga, aby nawigacja odbywała się w kontekście noopener, tak aby strona docelowa była wystarczająco odizolowana. Dyrektywy dotyczące fragmentów tekstu są stosowane tylko do ramki głównej. Oznacza to, że tekst nie jest wyszukiwany w elementach iframe, a nawigacja w elementach iframe nie wywołuje fragmentu tekstu.

Prywatność

Ważne jest, aby implementacje specyfikacji fragmentów tekstu nie ujawniały, czy fragment tekstu został znaleziony na stronie. Fragmenty elementów są w pełni kontrolowane przez autora oryginalnej strony, ale fragmenty tekstu może tworzyć każdy. Pamiętasz, jak w moim przykładzie nie było możliwości utworzenia linku do nagłówka ECMAScript Modules in Web Workers, ponieważ element <h1> nie miał atrybutu id, ale każdy, w tym ja, mógł po prostu utworzyć link do dowolnego miejsca, starannie tworząc fragment tekstu?

Wyobraźmy sobie, że prowadzę nieuczciwą sieć reklamową evil-ads.example.com. Wyobraźmy sobie dalej, że w jednym z elementów iframe reklamy dynamicznie utworzyłem ukryty element iframe współdzielenia do dating.example.com z adresem URL fragmentu tekstu dating.example.com#:~:text=Log%20Out, gdy użytkownik wejdzie w interakcję z reklamą. Jeśli znajdę tekst „Wyloguj się”, będę wiedzieć, że ofiara jest zalogowana w usłudze dating.example.com, co mogę wykorzystać do profilowania użytkownika. Ponieważ naiwna implementacja fragmentów tekstu może zdecydować, że udane dopasowanie powinno spowodować przełączenie fokusu, na stronie evil-ads.example.com mogę nasłuchiwać zdarzenia blur i w ten sposób dowiedzieć się, kiedy nastąpiło dopasowanie. W Chrome zaimplementowaliśmy fragmenty tekstu w taki sposób, że taki scenariusz nie może się wydarzyć.

Inny atak może polegać na wykorzystaniu ruchu w sieci na podstawie pozycji przewijania. Załóżmy, że mam dostęp do logów ruchu w sieci mojej ofiary, na przykład jako administrator firmowego intranetu. Wyobraźmy sobie, że istnieje długi dokument działu kadr zatytułowany Co zrobić, jeśli cierpisz na…, a następnie lista schorzeń, takich jak wypalenie, stany lękowe itp. Mogę umieścić piksel śledzący obok każdego elementu na liście. Jeśli następnie ustalę, że wczytanie dokumentu następuje w tym samym czasie co wczytanie piksela śledzącego obok np. elementu wypalenie, jako administrator intranetu mogę stwierdzić, że pracownik kliknął link do fragmentu tekstu z :~:text=burn%20out, który według niego był poufny i niewidoczny dla nikogo. Ponieważ ten przykład jest nieco naciągany, a jego wykorzystanie wymaga spełnienia bardzo konkretnych warunków wstępnych, zespół ds. bezpieczeństwa Chrome ocenił, że ryzyko związane z wdrożeniem przewijania podczas nawigacji jest możliwe do opanowania. Inne klienty użytkownika mogą zamiast tego wyświetlać element interfejsu do ręcznego przewijania.

W przypadku witryn, które chcą zrezygnować z tej funkcji, Chromium obsługuje wartość nagłówka Document Policy, którą mogą wysyłać, aby klienty użytkownika nie przetwarzały adresów URL fragmentów tekstu.

Document-Policy: force-load-at-top

Wyłączanie fragmentów tekstu

Najprostszym sposobem wyłączenia tej funkcji jest użycie rozszerzenia, które może wstrzykiwać nagłówki odpowiedzi HTTP, np. ModHeader (nie jest to usługa Google), aby wstawić nagłówek odpowiedzi (nie żądania) w ten sposób:

Document-Policy: force-load-at-top

Innym, bardziej skomplikowanym sposobem na rezygnację jest użycie ustawienia Enterprise ScrollToTextFragmentEnabled. Aby to zrobić w systemie macOS, wklej w terminalu to polecenie.

defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false

W przypadku systemu Windows postępuj zgodnie z dokumentacją na stronie pomocy Centrum pomocy Google Chrome Enterprise.

W przypadku niektórych wyszukiwań wyszukiwarka Google wyświetla szybką odpowiedź lub podsumowanie z fragmentem treści z odpowiedniej witryny. Te fragmenty z odpowiedzią najczęściej wyświetlają się, gdy wyszukiwanie ma formę pytania. Kliknięcie fragmentu z odpowiedzią przenosi użytkownika bezpośrednio do tekstu fragmentu z odpowiedzią na źródłowej stronie internetowej. Dzieje się tak dzięki automatycznie tworzonym adresom URL fragmentów tekstu.

Strona wyników wyszukiwania Google z fragmentem z odpowiedzią. Na pasku stanu wyświetla się adres URL fragmentu tekstu.
Po kliknięciu odpowiednia sekcja strony zostanie przewinięta do widoku.

Podsumowanie

Adres URL fragmentów tekstu to zaawansowana funkcja umożliwiająca tworzenie linków do dowolnego tekstu na stronach internetowych. Społeczność naukowa może go używać do podawania bardzo dokładnych cytatów lub linków referencyjnych. Wyszukiwarki mogą go używać do precyzyjnego linkowania do wyników tekstowych na stronach. Serwisy społecznościowe mogą używać tej funkcji, aby umożliwić użytkownikom udostępnianie określonych fragmentów strony internetowej zamiast niedostępnych zrzutów ekranu. Mam nadzieję, że zaczniesz używać adresów URL fragmentów tekstu i będą one dla Ciebie tak samo przydatne jak dla mnie. Pamiętaj, aby zainstalować rozszerzenie przeglądarki Link to Text Fragment.

Podziękowania

Funkcję Text Fragments wdrożyli i opisali Nick Burris i David Bokan, a wkład w jej rozwój miał też Grant Wang. Dziękujemy Joe Medleyowi za dokładne sprawdzenie tego dokumentu.