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

Fragmenty tekstu umożliwiają określenie fragmentu tekstu w fragmentach 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 to ważna wersja. Zawierała ona wiele długo oczekiwanych funkcji, takich jak moduły ECMAScript w instancjach roboczych Web, nullish coalescing, opcjonalne łańcuchy itp. Jak zwykle, o wypuszczeniu aktualizacji poinformowano w poście na blogu Chromium. Wycinek z postu 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ą czerwone pola. Są one wynikiem uruchomienia w Narzędziach deweloperskich 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 mogę umieścić precyzyjny link do dowolnego elementu wyróżnionego czerwonym polem. Następnie używam go w haszu adresu URL strony. Załóżmy, że chcę utworzyć precyzyjny link do pola Prześlij nam opinię w Forach na temat usług w sekcji „Uwagi” – 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 deweloperów, element, o którym mowa, 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. To jest problem, który rozwiązują fragmenty tekstu.

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

Fragmenty tekstu

Propozycja Fragmenty tekstowe umożliwia określenie fragmentu tekstu w haszu 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 hash #, a potem :~:text=start, co oznacza tekst zakodowany w postaci procentów, do którego chcę utworzyć 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 miał postać:

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 przeglądarce obsługującej tę funkcję, takiej jak Chrome, fragment tekstu zostanie wyróżniony i przesunięty w widoczne miejsce:

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

startend

Co zrobić, jeśli chcę utworzyć link do całej sekcji 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 bardzo długi.

Na szczęście istnieje lepszy sposób. Zamiast całego tekstu mogę zaznaczyć wybrany tekst za pomocą składni start,end. 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 następnie 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 adres URL poniżej wczytuje stronę wiki o hasło „kot” i przewija do treści wymienionych w parametrze „text”.
Wycinek z posta na blogu o ogłoszeniu Text Fragments.

Zwróć uwagę, że na zrzucie ekranu powyżej słowo „text” pojawia się 4 razy. Czwarta pozycja jest napisana zieloną czcionką kodu. Jeśli chcę utworzyć link do tego konkretnego słowa, ustawiam start na text. Skoro słowo „tekst” to tylko jedno słowo, 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 „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 inne parametry, muszą być one zakodowane w postaci procentów i mogą zawierać więcej niż jedno 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, muszą one być oddzielone 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 wskazują 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 przecinka ,, aby nie były interpretowane jako część składni dyrektywy tekstowej.

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

Każda z wartości prefix-, start, end-suffix będzie pasować tylko do tekstu w pojedynczym elemencie 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 fragmentów tekstowych za pomocą rozszerzenia do 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 wskazówek i dokładnych instrukcji dotyczących generowania adresów URL fragmentów tekstu zawartych w specyfikacji. Udostępniamy rozszerzenie do przeglądarki typu open source o nazwie Link do fragmentu tekstu, które umożliwia linkowanie do dowolnego tekstu przez jego zaznaczenie i kliknięcie w menu kontekstowym opcji „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. Nie ma nic złego w tym, że oba te adresy URL są takie same, np. aby zapewnić alternatywne rozwiązanie na wypadek, gdyby oryginalny tekst na stronie uległ zmianie i fragment tekstu nie pasował już do oryginalnego. 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 opisane powyżej, specyfikacja Fragmenty tekstu wprowadza dyrektywę fragmentu. Dyrektywa fragmentu to część fragmentu adresu URL ograniczona sekwencją kodu :~:. Jest on zarezerwowany na potrzeby 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ółpracować. Instrukcje 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 można uzyskać dostęp tylko do odczytu właściwości fragmentDirective. Dyrektywa fragmentu to mechanizm, który umożliwia adresom URL określanie instrukcji kierowanych do przeglądarki, a nie do dokumentu. Ma to na celu uniknięcie bezpośredniej interakcji ze skryptem autora, aby można było dodawać instrukcje dotyczące przyszłych agentów użytkowników bez obaw o wprowadzenie zmian w dotychczasowych 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, gdy 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 systemowe dla mark). Arkusz stylów klienta 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 styl zastąpienia nie powodował problemów z dostępnością. Upewnij się też, że wyróżnienie wyraźnie odróżnia się od reszty treści.

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

Możliwość stosowania w ramach rozwiązania typu „polyfill”

Funkcję fragmentów tekstowych 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ą wbudowanej obsługi fragmentów tekstu, gdy funkcja jest implementowana w JavaScript.

Polyfill zawiera plik fragment-generation-utils.js, który możesz zaimportować i użyć do wygenerowania linków do fragmentów tekstowych. 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);
}

Pobieranie fragmentów tekstu na potrzeby analizy

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ć kodu poniżej, aby wyodrębnić odpowiednie informacje.

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

Bezpieczeństwo

Dyrektywy fragmentu tekstu są wywoływane tylko w przypadku pełnej (nie tej samej) nawigacji, która jest wynikiem aktywacji przez użytkownika. Ponadto nawigacja z innego miejsca niż miejsce docelowe wymaga kontekstu noopener, aby można było stwierdzić, że strona docelowa jest wystarczająco odizolowana. Polecenia fragmentu tekstu są stosowane tylko do ramki głównej. Oznacza to, że tekst nie będzie wyszukiwany w elementach iframe, a nawigacja w elementach iframe nie wywoła fragmentu tekstu.

Prywatność

Ważne jest, aby implementacje specyfikacji fragmentów tekstu nie ujawniały, 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 moich iframe’ów reklam dynamicznie utworzyłem ukryty iframe z innej domeny (dating.example.com) z adresem URL fragmentu tekstowego (dating.example.com#:~:text=Log%20Out), gdy użytkownik wejdzie w interakcję z reklamą. 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ż prosta implementacja Text Fragments może zdecydować, że dopasowanie powinno spowodować zmianę elementu aktywnego, w evil-ads.example.com mogę nasłuchiwać zdarzenia blur i w ten sposób wiedzieć, kiedy nastąpiło dopasowanie. W Chrome fragmenty tekstu zostały zaimplementowane w taki sposób, aby powyższy scenariusz nie mógł mieć miejsca.

Innym atakiem może być wykorzystanie ruchu sieciowego na podstawie pozycji przewijania. Załóżmy, że mam dostęp do logów ruchu sieciowego mojej ofiary, np. jako administrator firmowego intranetu. Wyobraź sobie, że istnieje długi dokument HR Co robić, jeśli cierpisz na…, a obok lista stanów takich jak wypalenie, lęk itp. Obok każdego elementu na liście można umieścić piksel śledzenia. Jeśli ustalę, że wczytywanie dokumentu zbiega się czasowo z wczytywaniem piksela śledzącego obok elementu wypalenie, mogę jako administrator intranecie 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. Ponieważ ten przykład jest nieco wymyślony i wymaga spełnienia bardzo konkretnych warunków wstępnych, zespół ds. bezpieczeństwa Chrome uznał, że ryzyko związane z wdrożeniem przewijania w celu nawigacji jest do opanowania. 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 wyłączenia tej funkcji jest użycie rozszerzenia, które może wstrzyknąć nagłówki odpowiedzi HTTP, np. 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 skomplikowanym sposobem rezygnacji jest użycie ustawienia dla organizacjiScrollToTextFragmentEnabled. 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ą na stronie 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 danego fragmentu z odpowiedzią bezpośrednio na stronie internetowej, z której ten fragment pochodzi. Jest to możliwe dzięki automatycznie tworzonym adresom URL fragmentów tekstowych.

Strona wyników wyszukiwarki Google z wyróżnionym fragmentem. 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 fragmentów tekstu i że będą one dla Ciebie tak samo przydatne jak dla mnie. Pamiętaj, aby zainstalować rozszerzenie przeglądarki Link do fragmentu tekstu.

Podziękowania

Fragmenty tekstu zostały zaimplementowane i określone przez Nicka BurrisaDavida Bokana, z udziałem Granta Wanga. Dziękujemy Joe Medley za dokładne sprawdzenie tego artykułu. Baner powitalny autorstwa Greg Rakozy na Unsplash.