修改網頁
擴充套件最常見的用例之一就是修改網頁。例如,一個擴充套件可能希望更改應用於頁面的樣式、隱藏特定的 DOM 節點或將額外的 DOM 節點注入頁面。
使用 WebExtensions API 可以透過兩種方式實現此目的:
- 宣告式地:定義一個匹配一組 URL 的模式,並將一組指令碼載入到 URL 與該模式匹配的頁面中。
- 程式設計式地:使用 JavaScript API,將指令碼載入到由特定標籤頁託管的頁面中。
無論哪種方式,這些指令碼都稱為內容指令碼,並且與構成擴充套件的其他指令碼不同。
- 它們只能訪問 WebExtension API 的一小部分。
- 它們可以直接訪問載入它們的網頁。
- 它們使用訊息傳遞 API 與擴充套件的其餘部分進行通訊。
在本文中,我們將介紹載入指令碼的這兩種方法。
修改匹配 URL 模式的頁面
首先,建立一個名為“modify-page”的新目錄。在該目錄中,建立一個名為“manifest.json”的檔案,內容如下:
{
"manifest_version": 2,
"name": "modify-page",
"version": "1.0",
"content_scripts": [
{
"matches": ["https://mdn.club.tw/*"],
"js": ["page-eater.js"]
}
]
}
content_scripts 鍵是如何將指令碼載入到匹配 URL 模式的頁面中。在這種情況下,content_scripts 指示瀏覽器將名為“page-eater.js”的指令碼載入到 https://mdn.club.tw/ 下的所有頁面中。
注意:由於 content_scripts 的 "js" 屬性是一個數組,因此您可以使用它將多個指令碼注入到匹配的頁面中。如果這樣做,頁面將共享相同的範圍,就像頁面載入的多個指令碼一樣,並且它們會按照在陣列中列出的順序載入。
注意:content_scripts 鍵還有一個 "css" 屬性,您可以使用它來注入 CSS 樣式表。
接下來,在“modify-page”目錄中建立一個名為“page-eater.js”的檔案,併為其新增以下內容:
document.body.textContent = "";
let header = document.createElement("h1");
header.textContent = "This page has been eaten";
document.body.appendChild(header);
現在 安裝擴充套件,然後訪問 https://mdn.club.tw/。頁面應該如下所示:

以程式設計方式修改頁面
如果您仍然想“吃掉”頁面,但只在使用者請求時才這樣做,該怎麼辦?讓我們更新此示例,以便在使用者單擊上下文選單項時注入內容指令碼。
首先,將“manifest.json”更新為包含以下內容:
{
"manifest_version": 2,
"name": "modify-page",
"version": "1.0",
"permissions": ["activeTab", "contextMenus"],
"background": {
"scripts": ["background.js"]
}
}
在這裡,我們刪除了 content_scripts 鍵,並添加了兩個新鍵:
permissions:要將指令碼注入頁面,我們需要修改頁面的許可權。activeTab許可權是一種臨時獲取當前活動標籤頁許可權的方法。我們還需要contextMenus許可權才能新增上下文選單項。background:我們使用它來載入一個持久的 “後臺指令碼”,名為background.js,我們將在其中設定上下文選單並注入內容指令碼。
讓我們建立這個檔案。在 modify-page 目錄中建立一個名為 background.js 的新檔案,併為其新增以下內容:
browser.contextMenus.create({
id: "eat-page",
title: "Eat this page",
});
browser.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "eat-page") {
browser.tabs.executeScript({
file: "page-eater.js",
});
}
});
在此指令碼中,我們建立了一個 上下文選單項,併為其指定了一個特定的 id 和 title(將在上下文選單中顯示的文字)。然後,我們設定了一個事件監聽器,以便當使用者單擊上下文選單項時,我們檢查它是否是我們 eat-page 項。如果是,我們使用 tabs.executeScript() API 將“page-eater.js”注入當前標籤頁。此 API 可選地接受標籤頁 ID 作為引數:我們省略了標籤頁 ID,這意味著指令碼被注入到當前活動標籤頁中。
此時,擴充套件應該如下所示:
modify-page/
background.js
manifest.json
page-eater.js
現在 重新載入擴充套件,開啟一個頁面(這次是任何頁面),啟用上下文選單,然後選擇“Eat this page”。

訊息
內容指令碼和後臺指令碼無法直接訪問彼此的狀態。但是,它們可以透過傳送訊息進行通訊。一方設定訊息監聽器,另一方可以向其傳送訊息。下表總結了雙方涉及的 API:
| 在內容指令碼中 | 在後臺指令碼中 | |
|---|---|---|
| 傳送訊息 |
browser.runtime.sendMessage()
|
browser.tabs.sendMessage()
|
| 接收訊息 |
browser.runtime.onMessage
|
browser.runtime.onMessage
|
注意:除了這種傳送一次性訊息的通訊方法外,您還可以使用 基於連線的方法來交換訊息。有關選擇建議,請參閱 選擇一次性訊息還是基於連線的訊息。
讓我們更新我們的示例,以展示如何從後臺指令碼傳送訊息。
首先,編輯 background.js,使其包含以下內容:
browser.contextMenus.create({
id: "eat-page",
title: "Eat this page",
});
function messageTab(tabs) {
browser.tabs.sendMessage(tabs[0].id, {
replacement: "Message from the extension!",
});
}
function onExecuted(result) {
let querying = browser.tabs.query({
active: true,
currentWindow: true,
});
querying.then(messageTab);
}
browser.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "eat-page") {
let executing = browser.tabs.executeScript({
file: "page-eater.js",
});
executing.then(onExecuted);
}
});
現在,在注入 page-eater.js 後,我們使用 tabs.query() 來獲取當前活動標籤頁,然後使用 tabs.sendMessage() 將訊息傳送到該標籤頁中載入的內容指令碼。訊息的有效負載為 {replacement: "Message from the extension!"}。
接下來,將 page-eater.js 更新如下:
function eatPageReceiver(request, sender, sendResponse) {
document.body.textContent = "";
let header = document.createElement("h1");
header.textContent = request.replacement;
document.body.appendChild(header);
}
browser.runtime.onMessage.addListener(eatPageReceiver);
現在,內容指令碼不再僅僅立即“吃掉”頁面,而是使用 runtime.onMessage 監聽訊息。當訊息到達時,內容指令碼執行與之前基本相同的程式碼,只是替換文字取自 request.replacement。
由於 tabs.executeScript() 是一個非同步函式,為了確保在 page-eater.js 中新增監聽器後才傳送訊息,我們使用 onExecuted(),它將在 page-eater.js 執行後被呼叫。
注意:按 Ctrl+Shift+J(macOS 上為 Cmd+Shift+J)或 web-ext run --bc 開啟 瀏覽器控制檯以檢視後臺指令碼中的 console.log。
或者,使用 附加元件偵錯程式,它可以讓您設定斷點。目前沒有辦法 直接從 web-ext 啟動附加元件偵錯程式。
如果我們想從內容指令碼向後臺頁面傳送訊息,我們將使用 runtime.sendMessage() 而不是 tabs.sendMessage(),例如:
browser.runtime.sendMessage({
title: "from page-eater.js",
});
注意:這些示例都注入了 JavaScript;您也可以使用 tabs.insertCSS() 函式以程式設計方式注入 CSS。
瞭解更多
-
內容指令碼指南
-
content_scriptsmanifest 鍵 -
permissionsmanifest 鍵 -
使用
content_scripts的示例 -
使用
tabs.executeScript()的示例