使用 Tabs API

選項卡允許使用者在其瀏覽器視窗中開啟多個網頁,然後切換這些網頁。藉助 Tabs API,您可以操作這些選項卡,建立實用程式,為使用者提供使用選項卡的新方式,或實現擴充套件程式的功能。

在這篇操作指南文章中,我們將探討:

  • 使用 Tabs API 所需的許可權。
  • 使用 tabs.query 發現有關選項卡及其屬性的更多資訊。
  • 建立、複製、移動、更新、重新載入和移除選項卡。
  • 操作選項卡的縮放級別。
  • 操作選項卡的 CSS。

最後,我們將介紹 API 提供的其他一些雜項功能。

注意: 有些 Tab API 功能在其他地方有介紹。這些是可用於透過指令碼操作選項卡內容的方法(tabs.connecttabs.sendMessagetabs.executeScript)。如果您想了解這些方法的更多資訊,請參閱概念文章 內容指令碼 和操作指南 修改網頁

許可權與 Tabs API

對於大多數 Tabs API 功能,您不需要任何許可權;但是,也有一些例外情況:

以下是您可能在擴充套件程式的 manifest.json 檔案中請求 "tabs" 許可權的方式:

json
"permissions": [
  "<all_urls>",
  "tabs"
],

此請求允許您在使用者訪問的所有網站上使用所有 Tabs API 功能。還有另一種請求許可權以使用 tabs.executeScript()tabs.insertCSS() 的替代方法,即不需要主機許可權的 "activeTab"。此許可權提供與具有 <all_urls>"tabs" 相同的許可權,但有兩個限制:

  • 使用者必須透過擴充套件程式的瀏覽器或頁面操作、上下文選單或快捷鍵與擴充套件程式進行互動。
  • 它只在活動選項卡中授予許可權。

這種方法的好處是使用者不會收到許可權警告,提示您的擴充套件程式可以“訪問所有網站的資料”。這是因為 <all_urls> 許可權允許擴充套件程式隨時在任何選項卡中執行指令碼,而 "activeTab" 僅限於允許擴充套件程式在當前選項卡中執行使用者請求的操作。

發現有關選項卡及其屬性的更多資訊

有時您可能想獲取所有瀏覽器視窗中所有選項卡的列表。其他時候,您可能想查詢與某些特定條件匹配的選項卡子集,例如從特定選項卡開啟的選項卡或顯示特定域頁面的選項卡。一旦您有了選項卡列表,您可能想了解更多有關其屬性的資訊。

這就是 tabs.query() 的作用。單獨使用它來獲取所有選項卡,或者使用 queryInfo 物件來指定查詢條件(例如選項卡是否處於活動狀態、是否在當前視窗中,或者 17 個條件中的一個或多個),tabs.query() 返回一個包含有關選項卡資訊的 tabs.Tab 物件陣列。

如果您只想獲取當前選項卡的資訊,可以使用 tabs.getCurrent() 為該選項卡獲取一個 tabs.Tab 物件。如果您有選項卡的 ID,可以使用 tabs.get() 獲取其 tabs.Tab 物件。

操作方法示例

要了解 tabs.query()tabs.Tab 的用法,讓我們看看 tabs-tabs-tabs 示例如何將“切換到選項卡”列表新增到其工具欄按鈕彈出視窗中。

The tabs toolbar menu showing the switch to tap area

manifest.json

這是 manifest.json

json
{
  "browser_action": {
    "default_title": "Tabs, tabs, tabs",
    "default_popup": "tabs.html"
  },
  "description": "A list of methods you can perform on a tab.",
  "homepage_url": "https://github.com/mdn/webextensions-examples/tree/main/tabs-tabs-tabs",
  "manifest_version": 2,
  "name": "Tabs, tabs, tabs",
  "permissions": ["tabs"],
  "version": "1.0"
}

備註

  • tabs.htmlbrowser_action 中定義為 default_popup 每當使用者點選擴充套件程式的工具欄圖示時,它就會顯示。
  • 許可權包括 tabs。 這是支援選項卡列表功能所必需的,因為擴充套件程式讀取選項卡的標題以顯示在彈出視窗中。
tabs.html

tabs.html 定義了擴充套件程式彈出視窗的內容:

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="tabs.css" />
  </head>

  <body>
    <div class="panel">
      <div class="panel-section panel-section-header">
        <div class="text-section-header">Tabs-tabs-tabs</div>
      </div>

      <a href="#" id="tabs-move-beginning">
        Move active tab to the beginning of the window
      </a>
      <br />

      <!-- Define the other menu items -->

      <div class="switch-tabs">
        <p>Switch to tab</p>
        <div id="tabs-list"></div>
      </div>
    </div>

    <script src="tabs.js"></script>
  </body>
</html>

它執行以下操作:

  1. 宣告選單項。
  2. 宣告一個 ID 為 tabs-list 的空 div,用於包含選項卡列表。
  3. 呼叫 tabs.js
tabs.js

tabs.js 中,我們將看到如何構建選項卡列表並將其新增到彈出視窗中。

建立彈出視窗

首先,新增一個事件處理程式,在載入 tabs.html 時執行 listTabs()

js
document.addEventListener("DOMContentLoaded", listTabs);

listTabs() 所做的第一件事是呼叫 getCurrentWindowTabs()。在這裡,tabs.query() 用於獲取當前視窗中選項卡的 tabs.Tab 物件:

js
function getCurrentWindowTabs() {
  return browser.tabs.query({ currentWindow: true });
}

現在,listTabs() 已準備好為彈出視窗建立內容。

首先:

  1. 獲取 <div id="tabs-list"> 元素。
  2. 建立一個文件片段(用於構建列表)。
  3. 設定計數器。
  4. 清除 <div id="tabs-list"> 元素的內容。
js
function listTabs() {
  getCurrentWindowTabs().then((tabs) => {
    const tabsList = document.getElementById("tabs-list");
    const currentTabs = document.createDocumentFragment();
    const limit = 5;
    let counter = 0;

    tabsList.textContent = "";
    // ...
  });
}

接下來,我們將為每個選項卡建立連結:

  1. 遍歷 tabs.Tab 物件中的前 5 個專案。
  2. 對於每個專案,向文件片段新增一個超連結。
    • 連結的標籤(即其文字)使用選項卡的 title(或 id,如果它沒有 title)設定。
    • 連結的地址使用選項卡的 id 設定。
js
function listTabs() {
  getCurrentWindowTabs().then((tabs) => {
    // ...
    for (const tab of tabs) {
      if (!tab.active && counter <= limit) {
        const tabLink = document.createElement("a");

        tabLink.textContent = tab.title || tab.id;

        tabLink.setAttribute("href", tab.id);
        tabLink.classList.add("switch-tabs");
        currentTabs.appendChild(tabLink);
      }

      counter += 1;
    }
    // ...
  });
}

最後,將文件片段寫入 <div id="tabs-list"> 元素:

js
function listTabs() {
  getCurrentWindowTabs().then((tabs) => {
    // ...
    tabsList.appendChild(currentTabs);
  });
}

使用活動選項卡

另一個相關的示例功能是“提醒活動選項卡”資訊選項,它將活動選項卡的所有 tabs.Tab 物件屬性轉儲到警報中:

js
// Other if conditions...
if (e.target.id === "tabs-alert-info") {
  callOnActiveTab((tab) => {
    let props = "";
    for (const item in tab) {
      props += `${item} = ${tab[item]} \n`;
    }
    alert(props);
  });
}

其中 callOnActiveTab() 透過遍歷 tabs.Tab 物件查詢已設定活動的項來查詢活動選項卡物件:

js
document.addEventListener("click", (e) => {
  function callOnActiveTab(callback) {
    getCurrentWindowTabs().then((tabs) => {
      for (const tab of tabs) {
        if (tab.active) {
          callback(tab, tabs);
        }
      }
    });
  }
});

建立、複製、移動、更新、重新載入和移除選項卡

收集到有關選項卡的資訊後,您很可能會對它們做一些事情——要麼為使用者提供操作和管理選項卡的功能,要麼在您的擴充套件程式中實現功能。

以下函式可用:

注意: 這些函式都要求提供它們所操作的選項卡的 ID(或 ID 列表)。

而以下函式將作用於活動選項卡(如果未提供選項卡 id):

操作方法示例

tabs-tabs-tabs 示例使用了所有這些功能,除了更新選項卡的 URL。這些 API 的使用方式類似,因此我們將檢視其中一個更復雜的實現,即“將活動選項卡移動到視窗列表的開頭”選項。

但首先,這是該功能實際執行的演示:

manifest.json

所有這些功能都不需要許可權即可操作,因此 manifest.json 檔案中沒有需要特別強調的功能。

tabs.html

tabs.html 定義了彈出視窗中顯示的“選單”,其中包括“將活動選項卡移動到視窗列表的開頭”選項,幷包含一系列由視覺分隔符分組的 <a> 標籤。每個選單項都帶有一個 id,在 tabs.js 中用於確定請求哪個選單項。

html
<a href="#" id="tabs-move-beginning">
  Move active tab to the beginning of the window
</a>
<br />
<a href="#" id="tabs-move-end">Move active tab to the end of the window</a>
<br />

<div class="panel-section-separator"></div>

<a href="#" id="tabs-duplicate">Duplicate active tab</a><br />
<a href="#" id="tabs-reload">Reload active tab</a><br />
<a href="#" id="tabs-alert-info">Alert active tab info</a><br />
tabs.js

為了實現 tabs.html 中定義的“選單”,tabs.js 包含一個用於監聽 tabs.html 中點選事件的監聽器:

js
document.addEventListener("click", (e) => {
  function callOnActiveTab(callback) {
    getCurrentWindowTabs().then((tabs) => {
      for (const tab of tabs) {
        if (tab.active) {
          callback(tab, tabs);
        }
      }
    });
  }
});

然後,一系列 if 語句將查詢匹配被點選項的 id

此程式碼片段用於“將活動選項卡移動到視窗列表的開頭”選項:

js
if (e.target.id === "tabs-move-beginning") {
  callOnActiveTab((tab, tabs) => {
    let index = 0;
    if (!tab.pinned) {
      index = firstUnpinnedTab(tabs);
    }
    console.log(`moving ${tab.id} to ${index}`);
    browser.tabs.move([tab.id], { index });
  });
}

值得注意的是 console.log() 的使用。這使您能夠將資訊輸出到偵錯程式控制檯,這在解決開發過程中發現的問題時很有用。

Example of the console.log output, from the move tabs feature, in the debugging console

移動程式碼首先呼叫 callOnActiveTab(),後者又呼叫 getCurrentWindowTabs() 以獲取包含活動視窗選項卡的 tabs.Tab 物件。然後它遍歷該物件以查詢並返回活動選項卡物件:

js
function callOnActiveTab(callback) {
  getCurrentWindowTabs().then((tabs) => {
    for (const tab of tabs) {
      if (tab.active) {
        callback(tab, tabs);
      }
    }
  });
}

固定選項卡

選項卡的一個功能是使用者可以在視窗中“固定”選項卡。固定選項卡放置在選項卡列表的開頭,並且無法移動。這意味著選項卡可以移動到的最早位置是任何固定選項卡之後的第一個位置。因此,呼叫 firstUnpinnedTab() 透過遍歷 tabs 物件來查詢第一個未固定選項卡的位置:

js
function firstUnpinnedTab(tabs) {
  for (const tab of tabs) {
    if (!tab.pinned) {
      return tab.index;
    }
  }
}

我們現在擁有移動選項卡所需的一切:活動選項卡物件(從中可以獲取選項卡 id)以及要將選項卡移動到的位置。因此,我們可以實現移動:

js
browser.tabs.move([tab.id], { index });

複製、重新載入、建立和移除選項卡的其餘功能也以類似方式實現。

操作選項卡的縮放級別

下一組功能允許您獲取 (tabs.getZoom) 和設定 (tabs.setZoom) 選項卡內的縮放級別。您還可以檢索縮放設定 (tabs.getZoomSettings),但在撰寫本文時,在 Firefox 中尚無法設定這些設定 (tabs.setZoomSettings)。

縮放級別可以在 30% 到 500% 之間(表示為小數 0.35)。

在 Firefox 中,預設縮放設定為:

  • 預設縮放級別 100%.
  • 縮放模式: 自動(因此瀏覽器管理縮放級別的設定方式)。
  • 縮放更改的範圍: "per-origin",這意味著當您再次訪問某個站點時,它會採用您上次訪問時設定的縮放級別。

操作方法示例

tabs-tabs-tabs 示例包含縮放功能的三個演示:放大、縮小和重置縮放。這是該功能的實際操作:

讓我們來看看放大的實現方式。

manifest.json

所有縮放功能都不需要許可權,因此 manifest.json 檔案中沒有需要特別強調的功能。

tabs.html

我們已經討論了 tabs.html 如何定義此擴充套件程式的選項,提供縮放選項沒有做任何新的或獨特的事情。

tabs.js

tabs.js 首先定義了縮放程式碼中使用的幾個常量:

js
const ZOOM_INCREMENT = 0.2;
const MAX_ZOOM = 5;
const MIN_ZOOM = 0.3;
const DEFAULT_ZOOM = 1;

然後它使用我們之前討論的相同監聽器,以便它可以在 tabs.html 中處理點選事件。

對於放大功能,它執行:

js
// Other if conditions...
if (e.target.id === "tabs-add-zoom") {
  callOnActiveTab((tab) => {
    browser.tabs.getZoom(tab.id).then((zoomFactor) => {
      // The maximum zoomFactor is 5, it can't go higher
      if (zoomFactor >= MAX_ZOOM) {
        alert("Tab zoom factor is already at max!");
      } else {
        let newZoomFactor = zoomFactor + ZOOM_INCREMENT;
        // If the newZoomFactor is set to higher than the max accepted
        // it won't change, and does not alert that it's at maximum
        newZoomFactor = newZoomFactor > MAX_ZOOM ? MAX_ZOOM : newZoomFactor;
        browser.tabs.setZoom(tab.id, newZoomFactor);
      }
    });
  });
}

此程式碼使用 callOnActiveTab() 獲取活動選項卡的詳細資訊,然後 tabs.getZoom 獲取選項卡的當前縮放因子。當前縮放與定義的最小值 (MAX_ZOOM) 進行比較,如果選項卡已達到最大縮放,則發出警報。否則,縮放級別會增加,但限制在最大縮放範圍內,然後使用 tabs.getZoom 設定縮放。

操作選項卡的 CSS

Tabs API 提供的另一個重要功能是操作選項卡內 CSS 的能力——向選項卡新增新 CSS (tabs.insertCSS()) 或從選項卡中移除 CSS (tabs.removeCSS())。

例如,如果您想突出顯示某些頁面元素或更改頁面的預設佈局,這會很有用。

操作方法示例

apply-css 示例使用這些功能為活動選項卡中的網頁新增紅色邊框。這是該功能的實際操作:

讓我們看看它是如何設定的。

manifest.json

manifest.json 請求使用 CSS 功能所需的許可權。您需要以下兩者之一:

後者最有用,因為它允許擴充套件程式在從擴充套件程式的瀏覽器或頁面操作、上下文選單或快捷方式執行時,在活動選項卡中使用 tabs.insertCSS()tabs.removeCSS()

json
{
  "description": "Adds a page action to toggle applying CSS to pages.",

  "manifest_version": 2,
  "name": "apply-css",
  "version": "1.0",
  "homepage_url": "https://github.com/mdn/webextensions-examples/tree/main/apply-css",

  "background": {
    "scripts": ["background.js"]
  },

  "page_action": {
    "default_icon": "icons/off.svg"
  },

  "permissions": ["activeTab", "tabs"]
}

您會注意到除了 "activeTab" 之外還請求了 "tabs" 許可權。這個額外的許可權是為了使擴充套件程式的指令碼能夠訪問選項卡的 URL,其重要性我們稍後會看到。

manifest.json 檔案中的其他主要功能是:

  • 後臺指令碼,它在擴充套件程式載入後立即開始執行。
  • “頁面操作”,它定義了一個要新增到瀏覽器位址列的圖示。
background.js

在啟動時,background.js 設定了一些常量來定義要應用的 CSS、"頁面操作"的標題以及擴充套件程式將使用的協議列表:

js
const CSS = "body { border: 20px solid red; }";
const TITLE_APPLY = "Apply CSS";
const TITLE_REMOVE = "Remove CSS";
const APPLICABLE_PROTOCOLS = ["http:", "https:"];

首次載入時,擴充套件程式使用 tabs.query() 獲取當前瀏覽器視窗中所有選項卡的列表。然後它遍歷這些選項卡並呼叫 initializePageAction()

js
browser.tabs.query({}).then((tabs) => {
  for (const tab of tabs) {
    initializePageAction(tab);
  }
});

initializePageAction 使用 protocolIsApplicable() 來確定活動選項卡的 URL 是否是 CSS 可以應用到的協議之一:

js
function protocolIsApplicable(url) {
  const anchor = document.createElement("a");
  anchor.href = url;
  return APPLICABLE_PROTOCOLS.includes(anchor.protocol);
}

然後,如果示例可以作用於該選項卡,initializePageAction() 會將選項卡的 pageAction(導航欄)圖示和標題設定為“關閉”版本,然後使 pageAction 可見:

js
function initializePageAction(tab) {
  if (protocolIsApplicable(tab.url)) {
    browser.pageAction.setIcon({ tabId: tab.id, path: "icons/off.svg" });
    browser.pageAction.setTitle({ tabId: tab.id, title: TITLE_APPLY });
    browser.pageAction.show(tab.id);
  }
}

接下來,pageAction.onClicked 上的監聽器等待 pageAction 圖示被點選,並在點選時呼叫 toggleCSS

js
browser.pageAction.onClicked.addListener(toggleCSS);

toggleCSS() 獲取 pageAction 的標題,然後執行所描述的操作:

  • 對於“應用 CSS”:

    • pageAction 圖示和標題切換到“移除”版本。
    • 使用 tabs.insertCSS() 應用 CSS。
  • 對於“移除 CSS”:

    • pageAction 圖示和標題切換到“應用”版本。
    • 使用 tabs.removeCSS() 移除 CSS。
js
function toggleCSS(tab) {
  function gotTitle(title) {
    if (title === TITLE_APPLY) {
      browser.pageAction.setIcon({ tabId: tab.id, path: "icons/on.svg" });
      browser.pageAction.setTitle({ tabId: tab.id, title: TITLE_REMOVE });
      browser.tabs.insertCSS({ code: CSS });
    } else {
      browser.pageAction.setIcon({ tabId: tab.id, path: "icons/off.svg" });
      browser.pageAction.setTitle({ tabId: tab.id, title: TITLE_APPLY });
      browser.tabs.removeCSS({ code: CSS });
    }
  }

  browser.pageAction.getTitle({ tabId: tab.id }).then(gotTitle);
}

最後,為確保每次選項卡更新後 pageAction 都是有效的,tabs.onUpdated 上的監聽器會在每次選項卡更新時呼叫 initializePageAction(),以檢查選項卡是否仍在使用 CSS 可以應用的協議。

js
browser.tabs.onUpdated.addListener((id, changeInfo, tab) => {
  initializePageAction(tab);
});

其他一些有趣的功能

還有一些其他的 Tabs API 功能不屬於前面的任何部分:

  • 使用 tabs.captureVisibleTab 捕獲可見的選項卡內容。
  • 使用 tabs.detectLanguage 檢測選項卡中內容的主要語言。例如,這可以用於將擴充套件程式的 UI 語言與其執行的頁面語言匹配。

瞭解更多

如果您想了解有關 Tabs API 的更多資訊,請檢視: