Chrome扩展开发:打开新标签并滚动至指定位置(仅作用于目标标签)
问题描述
我想要开发一款Chrome扩展,用于记录当前页面的URL和滚动坐标,以便后续继续阅读。我使用chrome.tabs.create在新标签中打开目标URL,但卡在页面滚动步骤。我尝试通过chrome.tabs.onUpdated.addListener和chrome.scripting.executeScript注入JavaScript命令,但不知道如何仅对特定标签执行脚本(当前方法会作用于所有新打开的标签)。
现有代码
manifest.json
{ "manifest_version": 3, "name": "Relay Reader", "version": "1.0", "permissions": ["contextMenus", "activeTab", "tabs", "webRequest", "scripting"], "action": { "default_popup": "popup.html" }, "description": "Continue reading where you are interrupted", "icons": { "16": "images/icon-16.png", "32": "images/icon-32.png", "48": "images/icon-48.png", "128": "images/icon-128.png" }, "content_scripts": [ { "js": ["scripts/content.js"], "matches": ["http://*/*", "https://*/*"] } ], "background": { "service_worker": "./scripts/background.js" }, "host_permissions": [ "https://*/*", "http://*/*" ] }
background.js
chrome.runtime.onInstalled.addListener((tableId, changeInfo, tab) => { chrome.contextMenus.create({ id: 'addMark', title: "Bookmark Here", contexts: ['all'] }) }); chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { if (changeInfo.status == "complete") { chrome.scripting.executeScript({ target: {tabId: tabId}, func: () => {window.scrollTo(0, 400)} }); } }) function addMark(info, tab) { if (info.menuItemId == "addMark") { chrome.tabs.sendMessage(tab.id, { action: "getSiteInfo" }, (response) => { let {siteUrl: url, position: top} = response; chrome.tabs.create({url: "example.com"}, () => {}) }) } } chrome.contextMenus.onClicked.addListener(addMark);
content.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action == "getSiteInfo") { let top = window.scrollY; let url = window.location.href; sendResponse({siteUrl: url, position: top}); } })
解决方案
问题出在全局的chrome.tabs.onUpdated监听会对所有标签生效。我们可以通过记录自己创建的标签ID,仅对该标签执行滚动逻辑,或者直接在chrome.tabs.create的回调中等待标签加载完成后执行脚本。
方法一:在tabs.create回调中绑定临时监听
修改background.js中的addMark函数,创建标签后添加针对该标签的临时监听,执行滚动后立即移除:
function addMark(info, tab) { if (info.menuItemId == "addMark") { chrome.tabs.sendMessage(tab.id, { action: "getSiteInfo" }, (response) => { const {siteUrl: url, position: top} = response; // 创建目标标签 chrome.tabs.create({url: url}, (newTab) => { // 定义临时监听函数 const scrollToPosition = (tabId, changeInfo) => { if (tabId === newTab.id && changeInfo.status === "complete") { chrome.scripting.executeScript({ target: {tabId: tabId}, func: (scrollTop) => window.scrollTo(0, scrollTop), args: [top] // 传递记录的滚动位置 }); // 执行后移除监听,避免影响其他标签 chrome.tabs.onUpdated.removeListener(scrollToPosition); } }; // 添加监听 chrome.tabs.onUpdated.addListener(scrollToPosition); }); }) } }
同时删除原来全局的chrome.tabs.onUpdated监听代码,避免冲突。
方法二:维护待滚动标签列表(适合批量场景)
如果需要处理多个标记,可以维护一个存储标签ID和对应滚动位置的Map,在全局监听中判断并执行:
// 存储需要滚动的标签ID与对应位置 const pendingScrolls = new Map(); function addMark(info, tab) { if (info.menuItemId == "addMark") { chrome.tabs.sendMessage(tab.id, { action: "getSiteInfo" }, (response) => { const {siteUrl: url, position: top} = response; chrome.tabs.create({url: url}, (newTab) => { // 将标签ID和滚动位置存入Map pendingScrolls.set(newTab.id, top); }); }) } } chrome.tabs.onUpdated.addListener((tabId, changeInfo) => { if (changeInfo.status === "complete" && pendingScrolls.has(tabId)) { const scrollTop = pendingScrolls.get(tabId); chrome.scripting.executeScript({ target: {tabId: tabId}, func: (top) => window.scrollTo(0, top), args: [scrollTop] }); // 执行后从Map中移除 pendingScrolls.delete(tabId); } })
关键说明
- 两种方法都避免了全局监听对所有标签的影响,仅针对我们创建的目标标签执行滚动。
- 使用
args参数传递记录的滚动位置,替代硬编码的400,实现动态滚动到上次阅读位置。 - 方法一的临时监听更轻量化,执行后立即销毁;方法二适合需要同时处理多个标签的场景。
内容的提问来源于stack exchange,提问作者Ma Twickenham
相关产品推荐
相关产品推荐

