您可以使用文字片段,在網址片段中指定文字片段。使用者前往含有這類文字片段的網址時,瀏覽器可以強調及/或引起使用者的注意。
片段 ID
Chrome 第 80 版是重大發布版本,其中包含許多備受期待的功能,例如 Web Workers 中的 ECMAScript 模組、空值協調、選用鏈結等。這個版本照常會透過 Chromium 網誌的網誌文章宣布。您可以在下方的螢幕截圖中查看網誌文章的摘錄。
![](https://web.dev/static/articles/text-fragments/image/egsW6tkKWYI8IHE6JyMZ.png?authuser=1&hl=zh-tw)
id
屬性的元素周圍有紅色方塊。你也許想知道這些紅色方框的意義。這些是在開發人員工具中執行以下程式碼片段後的結果。它會醒目顯示具有 id
屬性的所有元素。
document.querySelectorAll('[id]').forEach((el) => {
el.style.border = 'solid 2px red';
});
我可以透過片段 ID,為以紅色方塊標示的任何元素加入深層連結,接著在網頁網址的雜湊中使用這個片段。假設我要將深層連結中的「在我們的產品論壇」方塊中提供意見回饋,可以手寫網址 https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1
。如同在開發人員工具的「Elements」面板中,相關元素具有值為 HTML1
的 id
屬性。
![](https://web.dev/static/articles/text-fragments/image/whVXhhrYwA55S3i4J3l5.png?authuser=1&hl=zh-tw)
id
。如果我使用 JavaScript 的 URL()
建構函式剖析這個網址,就會看到不同的元件。請注意,hash
屬性含有 #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: ""
}
*/
雖然我必須開啟開發人員工具來找出元素的 id
,卻顯示了網頁上這個特定區段要由網誌文章作者連結到的機率。
如果我想連結至沒有 id
的項目,該怎麼辦?假設我要連結至「Web Workers 中的 ECMAScript 模組」標題。如以下螢幕截圖所示,有問題的 <h1>
沒有 id
屬性,所以無法連結至這個標題。而這正是文字片段解決的問題。
![](https://web.dev/static/articles/text-fragments/image/1g4rTS1q5LKHEHnDoF9o.png?authuser=1&hl=zh-tw)
id
的標題。文字片段
文字片段提案新增了在網址雜湊中指定文字片段的支援。使用者前往含有這類文字片段的網址時,可以強調和/或引起使用者的注意。
瀏覽器相容性
基於安全性原因,這項功能需要在 noopener
情境中開啟連結。因此,請務必在 <a>
錨點標記中加入 rel="noopener"
,或將 noopener
新增至 Window.open()
視窗功能清單。
start
文字片段的語法最簡單形式如下:雜湊符號 #
,後面接著 :~:text=
和 start
(代表我要連結的百分比編碼文字)。
#:~:text=start
舉例來說,如要連結至宣布 Chrome 80 版功能的網誌文章中的「ECMAScript Modules in Web Workers」標題,本範例的網址為:
文字片段強調這樣。在 Chrome 等支援的瀏覽器中按一下連結,系統會醒目顯示文字片段,並捲動至檢視畫面:
![](https://web.dev/static/articles/text-fragments/image/D3jwPrJlvN3FmJo3pADt.png?authuser=1&hl=zh-tw)
start
和end
如果我只想連結至標題為「ECMAScript Modules in Web Workers」整個部分,而非只是標題,對整個區段的文字進行百分比編碼時,會導致結果網址過長。
幸好現在有更好的方法。我可以使用 start,end
語法來針對所需文字建立外框,而不是整個文字。因此,我在所需文字的開頭指定幾個百分比編碼字詞,然後在所需文字結尾處指定幾個百分比編碼字詞,並以半形逗號 ,
分隔。
看起來會像這樣:
如果是 start
,我有 ECMAScript%20Modules%20in%20Web%20Workers
,然後是半形逗號 ,
,後面接著 ES%20Modules%20in%20Web%20Workers.
做為 end
。當您點選支援的瀏覽器 (例如 Chrome) 時,系統會醒目顯示整個部分並捲動至檢視畫面中:
![](https://web.dev/static/articles/text-fragments/image/2yTYmKnjHTnqXkcmHF1F.png?authuser=1&hl=zh-tw)
現在,您可能會好奇我選擇了start
和end
。實際上,簡短網址 https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript%20Modules,Web%20Workers.
的左右兩側只有兩個字詞,應該也能正常運作。比較 start
和 end
與先前的值。
如果我再來一點,但現在 start
和 end
都只有一個字,就代表我遇到問題了。網址 https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=ECMAScript,Workers.
現在更短,但醒目顯示的文字片段不再是原先需要的文字片段。系統會在第一個出現 Workers.
字詞時停止醒目顯示,但並非我想醒目顯示的內容。問題是,目前單字 start
和 end
值未能唯一識別所需要的部分:
![](https://web.dev/static/articles/text-fragments/image/GGbbtHBpsoFyubnISyZw.png?authuser=1&hl=zh-tw)
prefix-
和-suffix
為取得專屬連結,在 start
和 end
上使用足夠的值是很好的解決方案。但在某些情況下則不行。另一方面,為什麼我選擇了
Chrome 80 版本網誌文章做為例子?答案是,這個版本的「文字片段」推出了:
![網誌文章文字:文字網址片段。使用者或作者現在可以使用在網址中提供的文字片段,連結到網頁的特定部分。網頁載入時,瀏覽器會醒目顯示文字,並將片段捲動至檢視畫面中。舉例來說,下列網址會載入「Cat」的維基網頁,然後捲動至「text」參數所列的內容。](https://web.dev/static/articles/text-fragments/image/blog-post-text-text-url-67d2c112b0147.png?authuser=1&hl=zh-tw)
請注意,上述螢幕截圖中「text」一詞出現 4 次。第四次以綠色程式碼字型編寫。如果我想要連結至這個特定字詞,我會將 start
設為 text
。由於「text」(文字) 這個字是只有一個字,所以不可以有 end
。接下來該怎麼做?網址 https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=text
與標題中已有「Text」(文字) 字詞第一次出現比對相符:
![](https://web.dev/static/articles/text-fragments/image/nXxCskUwdCxwxejPSSZW.png?authuser=1&hl=zh-tw)
幸好有解決方法。在此情況下,我可以指定 prefix-
和 -suffix
。綠色代碼字型「text」之前的字詞是「the」,後方的字詞是「parameter」。從另外三個出現的「text」這個字詞,兩者前後沒有相同的字詞。獲得這些資訊後,我可以調整先前的網址,並新增 prefix-
和 -suffix
。和其他參數一樣,這些參數也需要百分比編碼,且可包含多個字詞。https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#:~:text=the-,text,-parameter
。如要讓剖析器清楚識別 prefix-
和 -suffix
,必須與 start
和選用的 end
分開,並以連字號 -
分隔。
![](https://web.dev/static/articles/text-fragments/image/J3L5BVSMmzGY6xdkabP6.png?authuser=1&hl=zh-tw)
完整語法
文字片段的完整語法如下所示。(方括號表示選用的參數)。
所有參數的值都必須經過百分比編碼。這點對破折號 -
、& 符號 &
和半形逗號 ,
字元而言特別重要,因此系統不會將這些字元解譯為文字指令語法的一部分。
#:~:text=[prefix-,]start[,end][,-suffix]
prefix-
、start
、end
和 -suffix
每個都只會比對單一區塊層級元素中的文字,但完整的 start,end
範圍可以橫跨多個區塊。例如,:~:text=The quick,lazy dog
會失敗,如以下範例所示,因為起始字串「The Quick」不會出現在一個不受干擾的區塊層級元素中:
<div>
The
<div></div>
quick brown fox
</div>
<div>jumped over the lazy dog</div>
但會如下例相符:
<div>The quick brown fox</div>
<div>jumped over the lazy dog</div>
使用瀏覽器擴充功能建立文字片段網址
手動建立文字片段網址相當繁瑣,需要確保網址不重複時更是如此。如果您真的想要,規格將提供一些提示,並列出文字片段網址產生確切步驟。我們提供一個名為 文字片段的連結的開放原始碼瀏覽器擴充功能,讓您選取內容,然後點選「連結」。 這項擴充功能適用於下列瀏覽器:
![](https://web.dev/static/articles/text-fragments/image/ASLtFCPoHvyTKrAtKAv4.png?authuser=1&hl=zh-tw)
一個網址含有多個文字片段
請注意,一個網址可以顯示多個文字片段。特定的文字片段必須以 &
字元分隔。以下是含有三個文字片段的連結範例: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
。
![](https://web.dev/static/articles/text-fragments/image/ffsq7aoSoVd9q6r5cquY.png?authuser=1&hl=zh-tw)
混合元素和文字片段
傳統元素片段可與文字片段合併。您當然可以在相同的網址中加入這兩者,例如在頁面的原始文字變更時提供有意義的備用方案,讓文字片段不再相符。連結至「產品論壇」部分的網址 https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html#HTML1:~:text=Give%20us%20feedback%20in%20our%20Product%20Forums.
同時包含元素片段 (HTML1
) 和文字片段 (text=Give%20us%20feedback%20in%20our%20Product%20Forums.
):
![](https://web.dev/static/articles/text-fragments/image/JRKCM6Ihrq8sgRZRiymr.png?authuser=1&hl=zh-tw)
片段指令
其中有一個我尚未說明的語法元素:片段指令 :~:
。為了避免與上述現有網址元素片段的相容性問題如上所示,文字片段規格中推出了片段指令。片段指令是網址片段的一部分,由程式碼序列 :~:
分隔。系統會將此參數保留給使用者代理程式指令 (例如 text=
) 使用,會在載入期間從網址中移除,讓作者指令碼無法直接與其互動。使用者代理程式操作說明也稱為指令。在具體情況中,text=
稱為「文字指令」。
功能偵測
如要偵測支援,請在 document
上測試唯讀 fragmentDirective
屬性。片段指令是一種網址機制,用於指定指向瀏覽器 (而非文件) 的指示。為了避免與作者指令碼直接互動,您可以在不擔心對現有內容導入破壞性變更的情況下,加入日後的使用者代理程式操作說明。日後新增的項目可能是翻譯提示的一個可能範例。
if ('fragmentDirective' in document) {
// Text Fragments is supported.
}
功能偵測功能主要適用於動態產生連結 (例如由搜尋引擎) 的情況,目的是避免將文字片段連結至不支援這項功能的瀏覽器。
設定文字片段樣式
根據預設,瀏覽器設定文字片段樣式的方式與設定 mark
樣式相同 (一般是黃色的,也就是 mark
的 CSS 系統顏色)。使用者代理程式樣式表包含的 CSS 內容如下所示:
:root::target-text {
color: MarkText;
background: Mark;
}
如您所見,瀏覽器會顯示虛擬選取器 ::target-text
,可用來自訂已套用的醒目顯示內容。舉例來說,您可以將文字片段設計為紅色背景上的黑色文字。一如往常,請務必檢查色彩對比,以免覆寫樣式造成無障礙功能問題,並確保醒目顯示功能實際上與其他內容不同。
:root::target-text {
color: black;
background-color: red;
}
可提取性
某種程度上,「文字片段」功能可以以折線形式呈現。我們提供 polyfill,這個擴充功能會在內部使用,如果瀏覽器並未內建支援文字片段 (在 JavaScript 中實作該功能),則可使用。
產生程式輔助文字片段連結
polyfill 包含檔案 fragment-generation-utils.js
,可用來匯入並用於產生文字片段連結。以下程式碼範例會大致說明這點:
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);
}
取得用於分析的文字片段
許多網站會使用片段進行轉送,因此瀏覽器會去除文字片段,以免網頁毀損。有已確認需要才能顯示連往網頁的文字片段連結 (例如為了進行分析),但我們提議的解決方案尚未實作。目前您可以使用以下程式碼擷取所需資訊,但目前可以解決這種問題。
new URL(performance.getEntries().find(({ type }) => type === 'navigate').name).hash;
安全性
只有在完整 (非相同網頁) 導覽為使用者啟用後,才能叫用文字片段指令。此外,如果導覽來源不是目的地,則導覽機制需要在 noopener
情境中完成,才能確保到達網頁已充分隔離。文字片段指令只會套用至主影格。這表示文字不會在 iframe 內搜尋,而 iframe 導覽也不會叫用文字片段。
隱私權
請注意,無論網頁是否找到文字片段,實作文字片段規格的實作並不會外洩。雖然元素片段完全受原始網頁作者的控制權,但任何人都可建立文字片段。也別忘了,在以上範例中,無法連結至「Web Workers 中的 ECMAScript 模組」標題,因為 <h1>
沒有 id
,但任何人 (包括我) 都能藉由精心製作文字片段,連結到任何位置嗎?
假設我放送了一個邪惡的廣告聯播網 evil-ads.example.com
。此外,在某個廣告 iframe 中,我會在使用者與廣告互動後,以文字片段網址 dating.example.com#:~:text=Log%20Out
動態建立隱藏的跨來源 iframe,並傳送至 dating.example.com
。如果找到「Log Out」字樣,我知道受害者目前已登入 dating.example.com
,我就可以據此進行使用者剖析。由於簡單的文字片段實作可能會判定成功比對結果應導致聚焦切換,因此在 evil-ads.example.com
上,我可以監聽 blur
事件,進而知道相符項目何時有相符項目。在 Chrome 中,我們導入了文字片段,以防上述情況發生。
另一種攻擊可能是根據捲動位置利用網路流量。假設我可以存取受害者的網路流量記錄
例如公司內部網路的管理員現在想像一下,有一個冗長的人力資源文件是「What to Do You Suffer From...」,以及「過熱」、「焦慮」等條件清單。我可以在清單上的每個項目旁放置一個追蹤像素。如果我確定載入文件時,會在旁邊同時載入追蹤像素 (比如「過剩」項目載入),那麼我就可以以內部網路管理員的身分,判斷員工曾點選含有 :~:text=burn%20out
的文字片段連結,確認員工誤認為是機密,其他人不會看到。本例有些概念起初,而且由於要符合「非常」特定的先決條件才能進行利用,因此 Chrome 安全性團隊已評估將導覽至導覽功能實作方便管理的風險。其他使用者代理程式可能會決定改為顯示手動捲動的 UI 元素。
對於想要選擇不採用的網站,Chromium 支援可傳送的文件政策標頭值,因此使用者代理程式不會處理文字片段網址。
Document-Policy: force-load-at-top
停用文字片段
停用此功能最簡單的方法是使用可插入 HTTP 回應標頭的擴充功能,例如 ModHeader (非 Google 產品) 來插入回應 (不是要求) 標頭,如下所示:
Document-Policy: force-load-at-top
另一個參與這項計畫的方式,是使用企業設定 ScrollToTextFragmentEnabled
。如要在 macOS 上執行此操作,請將下列指令貼到終端機中。
defaults write com.google.Chrome ScrollToTextFragmentEnabled -bool false
在 Windows 中,請按照 Google Chrome Enterprise 說明支援網站上的說明文件操作。
網頁搜尋中的文字片段
針對特定搜尋,Google 會透過相關網站的內容摘要提供快速解答或摘要。這類精選摘要最可能在搜尋形式為問題時顯示。按一下精選摘要後,系統會將使用者直接引導至來源網頁上的精選摘要文字。這項功能需要使用系統自動建立的文字片段網址。
![](https://web.dev/static/articles/text-fragments/image/KbZgnGxZOOymLxYPZyGH.png?authuser=1&hl=zh-tw)
![](https://web.dev/static/articles/text-fragments/image/4Q7zk9xBnb2uw8GRaLnU.png?authuser=1&hl=zh-tw)
結論
文字片段網址是一項強大的功能,可連結至網頁上的任意文字。學術社群可以使用這個功能提供高度準確的引用內容或參考資料連結。搜尋引擎可用以深層連結的方式,指向網頁上的文字結果。社群網站可以使用這個功能,讓使用者分享網頁的特定段落,而非無法存取的螢幕截圖。希望您可以開始使用文字片段網址,您會發現它們和我的功能很實用。請務必安裝文字片段連結瀏覽器擴充功能。
相關連結
特別銘謝
文字片段由 Nick Burris 和 David Bokan 導入及指定,並為 Grant Wang 所做的貢獻。感謝 Joe Medley 仔細閱讀這篇文章。Greg Rakozy 在 Unsplash 上提供的主頁橫幅。