You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展页面配色获取异常:切换页面后无法更新配色

问题描述

开发Chrome扩展时遇到问题:点击“Get Palette”按钮本应重新获取当前页面的5种流行颜色,但切换标签页或打开新页面后再次点击,仍显示之前页面的配色。初始页面点击按钮可正常获取,后续切换页面后结果不更新。

现有代码

<!DOCTYPE html>
<html>
  <head>
    <title>Color Palette</title>
  </head>
  <body>
    <h1>Color Palette</h1>
    <button id="get-palette">Get Palette</button>
    <main id="output"></main>
    <script src="popup.js"></script>
  </body>
</html>
document.addEventListener("DOMContentLoaded", function () {

    const btn = document.getElementById('get-palette')
    btn.addEventListener('click', () => {
        populateColors();
    })
});

function populateColors() {
    chrome.runtime.sendMessage({ method: "getColors" }, function (response) {
        const colorCounts = {};
        response.forEach((color) => {
            colorCounts[color] = (colorCounts[color] || 0) + 1;
        });
        const sortedColors = Object.keys(colorCounts).sort(
            (a, b) => colorCounts[b] - colorCounts[a]
        );

        const colorsContainer = document.getElementById("output");
        for (let i = 0; i < 5 && i < sortedColors.length; i++) {
            const colorBlock = document.createElement("div");
            colorBlock.style.backgroundColor = sortedColors[i];
            colorBlock.style.width = "50px";
            colorBlock.style.height = "50px";
            colorBlock.style.margin = "5px";
            colorBlock.style.border = "1px solid black";
            colorsContainer.appendChild(colorBlock);
        }
    });
}

background.js

let colors = [];
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.method == 'setColors') {
        colors = message.colors;
        console.log('Colors set:', colors);
    }
    else if (message.method == 'getColors') {
        sendResponse(colors);
        console.log('Colors sent:', colors);
    }
});

content_script.js

// Function to convert RGB color to HEX format
function rgbToHex(rgb) {
    const rgbValues = rgb.match(/\d+/g);
    const r = parseInt(rgbValues[0]);
    const g = parseInt(rgbValues[1]);
    const b = parseInt(rgbValues[2]);
    return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}

function renderColors() {
    const colorRegex = /#([0-9a-f]{3}){1,2}\b|\b((rgb|hsl)a?\([\d\s%,.]+\))/gi;
    const colors = [];
    const links = document.getElementsByTagName('link');

    for (let i = 0; i < links.length; i++) {
        const link = links[i];
        if (link.rel === 'stylesheet') {
            fetch(link.href)
                .then(response => response.text())
                .then(cssText => {
                    const colorValues = cssText.match(colorRegex);
                    if (colorValues) {
                        colorValues.forEach(colorValue => {
                            if (colorValue.startsWith("rgb")) {
                                const hexValue = rgbToHex(colorValue);
                                colors.push(hexValue);
                            } else {
                                colors.push(colorValue);
                            }
                        });
                    }
                    chrome.runtime.sendMessage({ 'method': 'setColors', 'colors': colors });
                })
                .catch(error => {
                    console.error('Error fetching CSS file:', error);
                });
        }
    }
}

renderColors();

问题根源

  1. content_script仅页面加载时执行一次:renderColors()只在页面首次加载时运行,切换标签页或刷新页面后不会重新触发,导致background中的颜色数据始终是第一次页面的结果。
  2. background全局变量无标签页区分:单一的colors数组存储所有页面的颜色,新页面的颜色不会覆盖旧数据,或者旧数据未被清空。
  3. popup点击未触发当前页面重新收集:点击按钮时直接读取background的旧数据,没有通知当前激活标签页的content_script重新抓取颜色。

修复方案

1. 修改popup.js:点击按钮时触发当前标签页收集颜色并清空旧结果

document.addEventListener("DOMContentLoaded", function () {
    const btn = document.getElementById('get-palette')
    btn.addEventListener('click', () => {
        // 清空之前的颜色块,避免重复追加
        const colorsContainer = document.getElementById("output");
        colorsContainer.innerHTML = '';
        
        // 获取当前激活标签页,通知content_script重新收集颜色
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
            chrome.tabs.sendMessage(tabs[0].id, {method: "fetchColors"}, () => {
                // 收集完成后获取并展示最新颜色
                populateColors();
            });
        });
    })
});

function populateColors() {
    chrome.runtime.sendMessage({ method: "getColors" }, function (response) {
        const colorCounts = {};
        response.forEach((color) => {
            colorCounts[color] = (colorCounts[color] || 0) + 1;
        });
        const sortedColors = Object.keys(colorCounts).sort(
            (a, b) => colorCounts[b] - colorCounts[a]
        );

        const colorsContainer = document.getElementById("output");
        for (let i = 0; i < 5 && i < sortedColors.length; i++) {
            const colorBlock = document.createElement("div");
            colorBlock.style.backgroundColor = sortedColors[i];
            colorBlock.style.width = "50px";
            colorBlock.style.height = "50px";
            colorBlock.style.margin = "5px";
            colorBlock.style.border = "1px solid black";
            colorsContainer.appendChild(colorBlock);
        }
    });
}

2. 修改content_script.js:监听popup消息,按需触发颜色收集

// Function to convert RGB color to HEX format
function rgbToHex(rgb) {
    const rgbValues = rgb.match(/\d+/g);
    const r = parseInt(rgbValues[0]);
    const g = parseInt(rgbValues[1]);
    const b = parseInt(rgbValues[2]);
    return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}

function renderColors() {
    const colorRegex = /#([0-9a-f]{3}){1,2}\b|\b((rgb|hsl)a?\([\d\s%,.]+\))/gi;
    const colors = [];
    const links = document.getElementsByTagName('link');
    
    // 用Promise.all等待所有CSS请求完成,确保颜色收集完整
    const fetchPromises = [];
    for (let i = 0; i < links.length; i++) {
        const link = links[i];
        if (link.rel === 'stylesheet') {
            fetchPromises.push(
                fetch(link.href)
                .then(response => response.text())
                .then(cssText => {
                    const colorValues = cssText.match(colorRegex);
                    if (colorValues) {
                        colorValues.forEach(colorValue => {
                            if (colorValue.startsWith("rgb")) {
                                const hexValue = rgbToHex(colorValue);
                                colors.push(hexValue);
                            } else {
                                colors.push(colorValue);
                            }
                        });
                    }
                })
                .catch(error => {
                    console.error('Error fetching CSS file:', error);
                })
            );
        }
    }

    // 所有请求完成后再发送颜色数据到background
    Promise.all(fetchPromises).then(() => {
        chrome.runtime.sendMessage({ 'method': 'setColors', 'colors': colors });
    });
}

// 监听来自popup的消息,触发颜色收集
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    if (message.method === "fetchColors") {
        renderColors();
        sendResponse({status: "color collection started"});
    }
});

// 页面加载时仍执行一次,确保初始数据可用
renderColors();

3. 优化background.js:按标签页ID存储颜色(避免多标签页冲突)

// 用对象存储不同标签页的颜色,key为标签页ID
let tabColors = {};

chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.method === 'setColors') {
        // 存储当前标签页的颜色
        if (sender.tab?.id) {
            tabColors[sender.tab.id] = message.colors;
            console.log(`Colors saved for tab ${sender.tab.id}:`, message.colors);
        }
    } else if (message.method === 'getColors') {
        // 异步获取当前激活标签页的颜色
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
            const activeTabId = tabs[0].id;
            sendResponse(tabColors[activeTabId] || []);
            console.log(`Colors sent for tab ${activeTabId}:`, tabColors[activeTabId] || []);
        });
        // 返回true表示将异步发送响应
        return true;
    }
});

说明

  • 修复后,每次点击“Get Palette”按钮都会触发当前激活标签页的content_script重新抓取页面CSS中的颜色,确保数据是最新的。
  • 按标签页ID存储颜色可以避免多标签页之间的颜色数据互相覆盖,提升扩展的多标签页兼容性。

内容的提问来源于stack exchange,提问作者Tyler Le

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 18:35:00