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

Fragmenty tekstu umożliwiają podanie fragmentu tekstu we fragmencie adresu URL. Gdy użytkownik przechodzi do adresu URL zawierającego taki fragment tekstu, przeglądarka może go podkreślić lub zwrócić na niego uwagę użytkownika.

Identyfikatory fragmentów

Chrome 80 okazał się premierą. Zawierała ona wiele długo oczekiwanych funkcji, takich jak moduły ECMAScript w instancjach roboczych Web Worker, nullish coalescing, opcjonalne łańcuchy itp. Jak zwykle, o wypuszczeniu aktualizacji poinformowano w poście na blogu Chromium. Fragment posta na blogu znajdziesz na zrzucie ekranu poniżej.

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

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

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

Dzięki identyfikatorowi fragmentu, który następnie wykorzystujemy w hash adresu URL strony, mogę umieścić precyzyjny link do dowolnego elementu zaznaczonego na czerwono. Załóżmy, że chcę utworzyć precyzyjny link do pola Prześlij nam opinię na forach poświęconych usługom, które znajduje się na stronie internetowej. Mogę to zrobić, wpisują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, element, którego dotyczy zgłoszenie, ma atrybut id o wartości HTML1.

Narzędzia dla programistów pokazujące id elementu.

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

Jednak fakt, że musiałem otworzyć Narzędzia dla programistów, aby znaleźć id elementu, wiele mówi o tym, że autor wpisu na blogu chciał utworzyć link do tej konkretnej sekcji strony.

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

Narzędzia dla programistów wyświetlają nagłówek bez id.

Fragmenty tekstu

W ofercie pakietowej Fragmenty tekstu dodaliśmy obsługę określania fragmentów tekstu w haszze adresu URL. Gdy użytkownik przechodzi do adresu URL zawierającego taki fragment tekstu, klient użytkownika może go podkreślić lub zwrócić na niego uwagę użytkownika.

Zgodność z przeglądarką

Obsługa przeglądarek

  • Chrome: 89.
  • Edge: 89.
  • Firefox: 131.
  • Safari Technology Preview: obsługiwane.

Źródło

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

start

W najprostszej formie składnia fragmentów tekstu wygląda tak: symbol skrótu #, po którym następuje znak :~:text=, a na końcu start oznacza tekst zakodowany za pomocą procentu, do którego ma prowadzić link.

#:~:text=start

Załóżmy na przykład, że chcę utworzyć link do nagłówka Moduły ECMAScript w Web Workerspoście na blogu ogłaszającym funkcje w Chrome 80. W tym przypadku 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 zobaczysz widok:

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

startend

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

Na szczęście istnieje lepszy sposób. Zamiast całego tekstu mogę zastosować składnię start,end, aby zaznaczyć wybrany tekst. Dlatego na początku podaję kilka słów zakodowanych procentowo, a na końcu kilka słów zakodowanych procentowo, rozdzielonych przecinkami ,.

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 potem ES%20Modules%20in%20Web%20Workers. jako end. Gdy klikniesz w przeglądarce obsługującej tę funkcję, takiej jak 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 startend. W zasadzie krótszy adres URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules,Web%20Workers. z 2 słowami po każdej stronie też byłby odpowiedni. Porównaj wartości startend z poprzednimi wartościami.

Jeśli pójdę o krok dalej i użyję tylko jednego słowa na określenie zarówno start, jak i end, możesz zobaczyć, że mam 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ż tym, który był pierwotnie wybrany. Podświetlenie kończy się w miejscu pierwszego wystąpienia słowa Workers., co jest prawidłowe, ale nie to, co chciałem podświetlić. Problem polega na tym, że żądana sekcja nie jest jednoznacznie identyfikowana przez obecne jednowyrazowe wartości start i end:

Nieprzewidziany fragment tekstu został przewinięty do widocznego obszaru i wyróżniony.

prefix--suffix

Użycie odpowiednio długich wartości atrybutów start i end to jedno z rozwiązań umożliwiających uzyskanie unikalnego linku. W niektórych sytuacjach nie jest to jednak możliwe. Dlaczego jako przykład wybrałem wpis na blogu o wersji Chrome 80? W tej wersji zostały wprowadzone fragmenty tekstu:

Tekst posta na blogu: tekstowe fragmenty adresów URL. Użytkownicy lub autorzy mogą teraz tworzyć linki do konkretnej części strony za pomocą fragmentu tekstu podanego w adresie URL. Po załadowaniu strony przeglądarka wyróżnia tekst i przewija fragment, aby był widoczny. Na przykład poniższy adres URL wczytuje stronę wiki dla słowa „kot” i przewija do treści podanej w parametrze „tekst”.
Wycinek z posta na blogu o ogłoszeniu Fragmentów tekstu

Zwróć uwagę, że na zrzucie ekranu powyżej słowo „text” pojawia się 4 razy. Czwarte wystąpienie jest zapisane zieloną czcionką. Jeśli chcę utworzyć link do tego konkretnego słowa, ustawiam start na text. Jako że słowo „tekst” to jest tylko jedno słowo, dlatego nie może występować 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 „Tekst” w nagłówku:

Fragment tekstu pasujący do pierwszego wystąpienia ciągu „Text”.

Na szczęście istnieje rozwiązanie. W takich przypadkach mogę określić prefix​--suffix. Słowo przed zielonym kodem „text” to „the”, a słowo po nim to „parameter”. Żadne z 3 innych wystąpień słowa „text” nie ma takich samych słów sąsiadujących. Mając tę wiedzę, mogę zmienić poprzedni adres URL i dodać prefix- oraz -suffix. Podobnie jak pozostałe parametry, one również muszą być zakodowane przy użyciu procentów 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 umożliwić parsującemu wyraźne rozpoznanie wartości prefix--suffix, należy je oddzielić od wartości start i opcjonalnej wartości end za pomocą myślnika -.

Fragment tekstu pasujący do wystąpienia ciągu „text”.

Pełna składnia

Poniżej przedstawiamy pełną składnię fragmentów tekstu. (Nawiasy kwadratowe oznaczają parametr opcjonalny). Wartości wszystkich parametrów muszą być zakodowane w postaci procentowej. Jest to szczególnie ważne w przypadku znaków myślnika -, znaku „&” & i pionowej kreski ,, aby nie były interpretowane jako część składni dyrektywy tekstowej.

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

Wartości prefix-, start, end i -suffix będą pasować tylko do tekstu w ramach jednego elementu na poziomie bloku, ale pełne zakresy start,end mogą obejmować wiele bloków. Na przykład w tym przykładzie :~:text=The quick,lazy dog nie będzie pasować, ponieważ początkowy ciąg znaków „The quick” nie pojawia się w pojedynczym, nieprzerwanym elemencie na poziomie bloku:

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

W tym przykładzie jednak pasuje:

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

Tworzenie adresów URL z fragmentami tekstu z rozszerzeniem przeglądarki

Tworzenie adresów URL fragmentów tekstu ręcznie jest żmudne, zwłaszcza jeśli chodzi o sprawdzanie, czy są one unikalne. Jeśli naprawdę chcesz, możesz skorzystać ze specyfikacji, która zawiera wskazówki i dokładne instrukcje generowania adresów URL fragmentów tekstu. Udostępniamy rozszerzenie do przeglądarki typu open source o nazwie Link do fragmentu tekstu, które umożliwia linkowanie do dowolnego tekstu. Wystarczy go zaznaczyć, a potem w menu kontekstowym kliknąć „Kopiuj link do zaznaczonego tekstu”. To rozszerzenie jest dostępne w tych przeglądarkach:

Link do fragmentu tekstu rozszerzenia przeglądarki.

Wiele fragmentów tekstu w 1 adresie URL

Pamiętaj, że w jednym adresie URL może się pojawiać wiele fragmentów tekstu. Poszczególne fragmenty tekstu muszą być rozdzielone znakiem „&”. 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 1 adresie URL.

Mieszanie elementów i fragmentów tekstu

Tradycyjne fragmenty elementów można łączyć z fragmentami tekstu. Możesz mieć oba te elementy pod tym samym adresem URL, np. aby uzyskać istotny tekst zastępczy w przypadku zmiany oryginalnego tekstu na stronie, który sprawi, że fragment tekstu nie będzie już dopasowany. Adres URL https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1:~:text=Give%20us%20feedback%20in%20our%20Product%20Forums. do sekcji Prześlij opinię na forach dotyczących produktów zawiera zarówno element (HTML1), jak i fragment tekstowy (text=Give%20us%20feedback%20in%20our%20Product%20Forums.):

Łączenie z fragmentem elementu i fragmentem tekstu.

Dyrektywa fragmentu

Jest jeden element składni, którego jeszcze nie wyjaśniłem: dyrektywa fragmentu :~:. Aby uniknąć problemów ze zgodnością z dotychczasowymi fragmentami elementów adresu URL, takich jak te pokazane powyżej, specyfikacja fragmentów tekstu wprowadza dyrektywę fragmentu. Dyrektywa fragment to część fragmentu adresu URL rozdzielonego sekwencją kodu :~:. Jest zarezerwowany dla instrukcji dotyczących klienta użytkownika, takich jak text=, i jest usuwany z adresu URL podczas wczytywania, aby skrypty autora nie mogły z nim bezpośrednio wchodzić w interakcję. Instrukcje dotyczące klienta użytkownika są też nazywane dyrektywami. W tym konkretnym przypadku text= nazywa się instrukcją tekstową.

Wykrywanie cech

Aby wykryć obsługę, przetestuj, czy w usługi document dotyczy tylko właściwości fragmentDirective. Dyrektywa dotycząca fragmentów to mechanizm określający instrukcje kierowane do przeglądarki, a nie do dokumentu. Ma to na celu unikanie bezpośredniej interakcji ze skryptem autora, dzięki czemu w przyszłości można dodawać instrukcje klienta użytkownika bez obawy o wprowadzenie zmian 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 w taki sam sposób, w jaki stylizują mark (zazwyczaj czarny na żółto, kolory systemu w usłudze porównywania cen). 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 pseudoselekcję ::target-text, za pomocą której można dostosować zastosowane podświetlenie. Możesz na przykład zaprojektować fragmenty tekstu jako czarny tekst na czerwonym tle. Jak zawsze, sprawdź kontrast kolorów, aby zastąpienie stylu nie powodowało problemów z ułatwieniami dostępu, i upewnij się, że wyróżnienie wyraźnie wyróżnia się na tle reszty treści.

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

Możliwość wielokrotnego wypełniania

Funkcję fragmentów tekstowych można w pewnym stopniu obsłużyć kodem polyfill. Udostępniamy plik polyfill, który jest używany wewnętrznie przez rozszerzenie na potrzeby przeglądarek, które nie mają wbudowanej obsługi fragmentów tekstu, w przypadku których ta funkcja jest zaimplementowany w kodzie JavaScript.

Polyfill zawiera plik fragment-generation-utils.js, który możesz zaimportować i wykorzystać do generowania linków z fragmentami tekstu. Poniżej znajdziesz opis tego procesu 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 kierowania, dlatego przeglądarki usuwają fragmenty tekstowe, aby nie uszkodzić tych stron. Uznano zapotrzebowanie na udostępnianie linków do stron w Fragmentach tekstowych, na przykład na potrzeby analityki, ale zaproponowane rozwiązanie nie zostało jeszcze zaimplementowane. Aby obejść ten problem, możesz użyć poniższego kodu do wyodrębnienia potrzebnych informacji.

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

Bezpieczeństwo

Dyrektywy dotyczące fragmentów tekstu są wywoływane tylko w przypadku pełnych nawigacji (nie na tej samej stronie), które są wynikiem aktywacji użytkownika. Poza tym nawigacja pochodząca z innego punktu początkowego niż miejsce docelowe będzie wymagać, aby nawigacja odbywała się w kontekście noopener, w taki sposób, aby strona docelowa była wystarczająco odizolowana. Dyrektywy dotyczące fragmentów tekstu są stosowane tylko w ramce głównej. Oznacza to, że tekst nie będzie wyszukiwany w elementach iframe, a nawigacja w elementach iframe nie będzie wywoływać fragmentu tekstu.

Prywatność

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

Wyobraź sobie, że zarządzam złą siecią reklamową. evil-ads.example.com Wyobraź sobie teraz, że w jednym z ramek reklamowych dynamicznie utworzyłem ukrytą ramkę między domenami, która po interakcji użytkownika z reklamą powoduje utworzenie adresu URL fragmentu tekstowego dating.example.com w ramce dating.example.com#:~:text=Log%20Out. Jeśli znajdziemy tekst „Wyloguj się”, wiemy, że ofiara jest obecnie zalogowana w dating.example.com, co można wykorzystać do stworzenia profilu użytkownika. Ponieważ naiwna implementacja fragmentów tekstu może zdecydować, że udane dopasowanie powinno spowodować przełączenie zaznaczenia, dlatego evil-ads.example.com mogłam nasłuchiwać zdarzenia blur i dzięki temu dowiedzieć się, kiedy wystąpiło dopasowanie. W Chrome wdrożyliśmy fragmenty tekstu w taki sposób, że nie ma to miejsca w powyższym scenariuszu.

Innym atakiem może być wykorzystanie ruchu sieciowego na podstawie pozycji przewijania. Załóżmy, że miałem dostęp do dzienników ruchu w sieci swojej ofiary, na przykład jako administrator firmowego intranetu. Wyobraźmy sobie teraz długi dokument działu kadr: Co zrobić, gdy cierpisz od..., z listą warunków, takich jak wypalenie, niepokój itd. Przy każdej pozycji na liście mogę umieścić piksel śledzący. Jeśli stwierdzę, że wczytywanie dokumentu występuje w tym samym czasie co wczytywanie piksela śledzącego obok np. elementu wypalenie, mogę jako administrator intranetu stwierdzić, że pracownik kliknął link do fragmentu tekstu z :~:text=burn%20out, który według niego mógł być poufny i niewidoczny dla nikogo. Ten przykład jest nieco skomplikowany od samego początku, a jego wykorzystanie wymaga spełnienia bardzo określonych warunków wstępnych, dlatego zespół ds. zabezpieczeń Chrome ocenił ryzyko wdrożenia nawigacji przewijanej jako łatwe do zarządzania. Inne przeglądarki mogą zamiast tego wyświetlić element interfejsu do ręcznego przewijania.

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

Document-Policy: force-load-at-top

Wyłączanie fragmentów tekstu

Najprostszym sposobem na wyłączenie tej funkcji jest użycie rozszerzenia, które może wstrzyknąć nagłówki odpowiedzi HTTP, na przykład ModHeader (nie jest to produkt Google), aby wstawić nagłówek odpowiedzi (nie żądania) w ten sposób:

Document-Policy: force-load-at-top

Innym, bardziej przydatnym sposobem rezygnacji jest użycie ustawienia biznesowego ScrollToTextFragmentEnabled. Aby to zrobić w systemie macOS, wklej to polecenie w terminalu.

defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false

W systemie Windows postępuj zgodnie z dokumentacją w witrynie pomocy Google Chrome Enterprise.

W przypadku niektórych wyszukiwań wyszukiwarka Google udostępnia szybką odpowiedź lub podsumowanie z fragmentem treści z odpowiedniej witryny. Te fragmenty z odpowiedzią są najczęściej wyświetlane, gdy wyszukiwane hasło ma postać pytania. Po kliknięciu fragmentu z odpowiedzią użytkownikowi wyświetla się tekst bezpośrednio na stronie źródłowej. Jest to możliwe dzięki automatycznie tworzonym adresom URL fragmentów tekstowych.

Strona wyników wyszukiwarki Google z fragmentem z odpowiedzią. Pasek stanu zawiera adres URL fragmentów tekstu.
Po kliknięciu odpowiednia sekcja strony zostanie wyświetlona.

Podsumowanie

Adres URL fragmentów tekstu to potężna funkcja umożliwiająca tworzenie linków do dowolnego tekstu na stronach internetowych. Naukowcy mogą używać tego narzędzia do podawania bardzo dokładnych cytatów lub linków do źródeł. Wyszukiwarki mogą używać tego linku do tworzenia linków do stron z tekstem. Strony społecznościowe mogą używać tej funkcji, aby umożliwić użytkownikom udostępnianie konkretnych fragmentów strony internetowej zamiast niedostępnych zrzutów ekranu. Mam nadzieję, że zaczniesz używać adresów URL z fragmentami tekstu i będą dla Ciebie równie przydatne. Pamiętaj, by zainstalować rozszerzenie przeglądarki Link to Text Fragment.

Podziękowania

Fragmenty tekstu wdrożyli i określili Mikołaj Burris i David Bokan przy pomocy Granta Wanga. Dziękujemy Joe Medley za dokładne sprawdzenie tego artykułu. Baner powitalny autorstwa Greg Rakozy na Unsplash.