Chrome.sidePanel API 详解
chrome.sidePanel API 是 Chrome 114+ 版本引入的新功能,允许扩展在浏览器侧边栏中显示自定义内容。这是一个非常强大的 UI 扩展方式。
一、基础配置
1. Manifest.json 配置
{
"manifest_version": 3,
"name": "Side Panel Demo",
"version": "1.0",
"permissions": [
"sidePanel"
],
"side_panel": {
"default_path": "sidepanel.html"
},
"action": {
"default_title": "打开侧边栏"
}
}
2. 创建侧边栏页面 (sidepanel.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Side Panel</title>
<style>
body {
width: 100%;
min-height: 100vh;
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
background: #f5f5f5;
}
.container {
max-width: 100%;
}
h1 {
color: #333;
font-size: 20px;
}
button {
padding: 10px 20px;
background: #4285f4;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #357ae8;
}
</style>
</head>
<body>
<div class="container">
<h1>侧边栏面板</h1>
<p>这是一个侧边栏扩展示例</p>
<button id="myButton">点击我</button>
</div>
<script src="sidepanel.js"></script>
</body>
</html>
二、核心 API 方法
1. chrome.sidePanel.setOptions()
设置侧边栏的行为和显示选项。
// 为所有标签页设置侧边栏
chrome.sidePanel.setOptions({
path: 'sidepanel.html',
enabled: true
});
// 为特定标签页设置侧边栏
chrome.sidePanel.setOptions({
tabId: tabId,
path: 'custom-panel.html',
enabled: true
});
// 禁用特定标签页的侧边栏
chrome.sidePanel.setOptions({
tabId: tabId,
enabled: false
});
参数说明:
path: 侧边栏 HTML 文件路径tabId: 可选,指定标签页 IDenabled: 是否启用侧边栏
2. chrome.sidePanel.getOptions()
获取侧边栏的当前配置。
// 获取全局侧边栏选项
chrome.sidePanel.getOptions({}, (options) => {
console.log('全局侧边栏配置:', options);
console.log('路径:', options.path);
console.log('是否启用:', options.enabled);
});
// 获取特定标签页的侧边栏选项
chrome.sidePanel.getOptions({
tabId: tabId
}, (options) => {
console.log('标签页侧边栏配置:', options);
});
// 使用 Promise (推荐)
const options = await chrome.sidePanel.getOptions({});
console.log(options);
3. chrome.sidePanel.open()
打开侧边栏(Chrome 116+)。
// 在当前窗口打开侧边栏
chrome.sidePanel.open({
windowId: windowId
});
// 在指定标签页所在窗口打开侧边栏
chrome.sidePanel.open({
tabId: tabId
});
// 使用示例:点击扩展图标时打开
chrome.action.onClicked.addListener(async (tab) => {
await chrome.sidePanel.open({ windowId: tab.windowId });
});
4. chrome.sidePanel.setPanelBehavior()
设置侧边栏的行为模式(Chrome 116+)。
// 设置侧边栏在所有标签页间保持打开
chrome.sidePanel.setPanelBehavior({
openPanelOnActionClick: true
});
// 示例:自动在点击扩展图标时打开
chrome.sidePanel.setPanelBehavior({
openPanelOnActionClick: true
}).then(() => {
console.log('侧边栏行为已设置');
});
参数说明:
openPanelOnActionClick: 布尔值,点击扩展图标时是否自动打开侧边栏
5. chrome.sidePanel.getOptions() - 高级用法
// 检查侧边栏是否已启用
async function isSidePanelEnabled(tabId) {
const options = await chrome.sidePanel.getOptions({ tabId });
return options.enabled;
}
// 切换侧边栏启用状态
async function toggleSidePanel(tabId) {
const options = await chrome.sidePanel.getOptions({ tabId });
await chrome.sidePanel.setOptions({
tabId,
enabled: !options.enabled
});
}
三、实用场景示例
示例 1: 根据 URL 显示不同侧边栏
// background.js 或 service worker
chrome.tabs.onUpdated.addListener(async (tabId, info, tab) => {
if (info.status === 'complete') {
const url = tab.url || '';
if (url.includes('github.com')) {
// GitHub 页面显示代码审查工具
await chrome.sidePanel.setOptions({
tabId,
path: 'github-panel.html',
enabled: true
});
} else if (url.includes('youtube.com')) {
// YouTube 页面显示视频笔记工具
await chrome.sidePanel.setOptions({
tabId,
path: 'youtube-panel.html',
enabled: true
});
} else {
// 其他页面使用默认侧边栏
await chrome.sidePanel.setOptions({
tabId,
path: 'sidepanel.html',
enabled: true
});
}
}
});
示例 2: 侧边栏与内容脚本通信
// sidepanel.js - 侧边栏脚本
document.getElementById('analyzeBtn').addEventListener('click', async () => {
// 获取当前活动标签页
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
// 向内容脚本发送消息
chrome.tabs.sendMessage(tab.id, {
action: 'analyzePage'
}, (response) => {
if (response) {
document.getElementById('result').textContent =
`页面分析结果: ${response.data}`;
}
});
});
// content.js - 内容脚本
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'analyzePage') {
// 分析页面内容
const wordCount = document.body.innerText.split(/\s+/).length;
const linkCount = document.querySelectorAll('a').length;
sendResponse({
data: `字数: ${wordCount}, 链接数: ${linkCount}`
});
}
return true; // 异步响应
});
示例 3: 显示当前标签页信息
// sidepanel.js
async function displayCurrentTabInfo() {
const [tab] = await chrome.tabs.query({
active: true,
currentWindow: true
});
const infoDiv = document.getElementById('tabInfo');
infoDiv.innerHTML = `
<h2>当前标签页信息</h2>
<p><strong>标题:</strong> ${tab.title}</p>
<p><strong>URL:</strong> ${tab.url}</p>
<p><strong>ID:</strong> ${tab.id}</p>
<p><strong>窗口ID:</strong> ${tab.windowId}</p>
`;
}
// 页面加载时显示信息
document.addEventListener('DOMContentLoaded', displayCurrentTabInfo);
// 监听标签页切换
chrome.tabs.onActivated.addListener(displayCurrentTabInfo);
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete') {
displayCurrentTabInfo();
}
});
示例 4: 书签管理侧边栏
// sidepanel.js
async function displayBookmarks() {
const bookmarks = await chrome.bookmarks.getTree();
const container = document.getElementById('bookmarkList');
function renderBookmarks(nodes, level = 0) {
let html = '';
for (const node of nodes) {
if (node.url) {
html += `
<div style="padding-left: ${level * 20}px; margin: 5px 0;">
<a href="${node.url}" target="_blank">${node.title}</a>
</div>
`;
} else if (node.children) {
html += `
<div style="padding-left: ${level * 20}px; margin: 5px 0;">
<strong>${node.title}</strong>
</div>
`;
html += renderBookmarks(node.children, level + 1);
}
}
return html;
}
container.innerHTML = renderBookmarks(bookmarks);
}
document.addEventListener('DOMContentLoaded', displayBookmarks);
示例 5: 笔记侧边栏(带存储)
// sidepanel.js
const noteArea = document.getElementById('noteArea');
const saveBtn = document.getElementById('saveNote');
// 加载笔记
async function loadNotes() {
const [tab] = await chrome.tabs.query({
active: true,
currentWindow: true
});
const key = `note_${tab.url}`;
const result = await chrome.storage.local.get(key);
noteArea.value = result[key] || '';
}
// 保存笔记
saveBtn.addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({
active: true,
currentWindow: true
});
const key = `note_${tab.url}`;
await chrome.storage.local.set({
[key]: noteArea.value
});
// 显示保存成功提示
const toast = document.createElement('div');
toast.textContent = '保存成功!';
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #4caf50;
color: white;
padding: 10px 20px;
border-radius: 4px;
`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2000);
});
// 标签页切换时重新加载笔记
chrome.tabs.onActivated.addListener(loadNotes);
loadNotes();
示例 6: 翻译侧边栏
// sidepanel.js
const translateBtn = document.getElementById('translate');
const resultDiv = document.getElementById('result');
translateBtn.addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({
active: true,
currentWindow: true
});
// 获取页面选中的文本
const response = await chrome.tabs.sendMessage(tab.id, {
action: 'getSelectedText'
});
if (response && response.text) {
// 这里可以调用翻译 API
resultDiv.textContent = `选中文本: ${response.text}`;
// 实际应用中这里应该调用翻译服务
} else {
resultDiv.textContent = '请先在页面中选择文本';
}
});
// content.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'getSelectedText') {
const selectedText = window.getSelection().toString();
sendResponse({ text: selectedText });
}
});
四、最佳实践
1. 响应式设计
/* sidepanel.css */
body {
width: 100%;
min-width: 280px; /* 侧边栏最小宽度 */
max-width: 100%;
padding: 16px;
box-sizing: border-box;
}
/* 适配不同宽度 */
@media (max-width: 400px) {
body {
padding: 12px;
font-size: 14px;
}
}
2. 性能优化
// 使用节流避免频繁更新
function throttle(func, delay) {
let timeoutId;
return function(...args) {
if (!timeoutId) {
timeoutId = setTimeout(() => {
func.apply(this, args);
timeoutId = null;
}, delay);
}
};
}
// 应用到标签页更新监听
const updateSidePanel = throttle(async () => {
// 更新侧边栏内容
}, 500);
chrome.tabs.onUpdated.addListener(updateSidePanel);
3. 错误处理
async function safeSetOptions(options) {
try {
await chrome.sidePanel.setOptions(options);
} catch (error) {
console.error('设置侧边栏选项失败:', error);
// 回退到默认行为
await chrome.sidePanel.setOptions({
path: 'sidepanel.html',
enabled: true
});
}
}
五、注意事项
-
浏览器版本要求: Chrome 114+ 支持基础功能,Chrome 116+ 支持
open()和setPanelBehavior() -
权限声明: 必须在 manifest.json 中声明
sidePanel权限 -
生命周期: 侧边栏页面在关闭时会被销毁,重新打开时会重新加载
-
通信方式: 使用
chrome.runtime.sendMessage和chrome.tabs.sendMessage进行通信 -
尺寸限制: 侧边栏宽度由用户拖动调整,扩展无法控制
-
上下文: 侧边栏运行在独立的上下文中,无法直接访问页面 DOM
-
性能考虑: 避免在侧边栏中执行大量计算,可以将任务委托给 service worker
-
样式建议: 遵循 Chrome 的设计规范,使用浅色背景和清晰的层次结构
chrome.sidePanel API 为扩展提供了更好的用户体验,特别适合需要持续展示信息或工具的场景,如笔记、翻译、书签管理、开发工具等。