在Chrome扩展CSS注入期间闪烁

Flickering during CSS injection in Chrome Extension

本文关键字:闪烁 注入 CSS Chrome 扩展      更新时间:2023-09-26

我有一个插件,通过注入一个CSS文件完全改变一个网站的外观。它可以工作,但在新外观出现之前的一瞬间,旧外观就在那里,因此导致每次页面加载时"闪烁"。新的外观改变了背景颜色等,所以切换非常明显。(我只在台式机上注意到这一点,在笔记本电脑上没有。我不知道为什么,但是其他用户也报告了这一点,也许更快的计算机使页面显示比注入CSS更快?)CSS注入需要发生,所以注入的CSS是最重要的(如在最后)。

我尝试了什么导致这个问题(代码之后):

  1. Manifest:确保css在web_accessible_resources
  2. Manifest:直接在content_scripts
  3. 中注入
  4. Manifest:通过javascript从content_scripts
  5. Manifest:确保所有的内容脚本都运行在document_start
  6. Manifest:尝试从后台页面上运行的脚本运行注入
  7. JS注入定时:为DOMSubtreeModified
  8. 添加事件监听器
  9. JS注入定时:为添加事件监听器chrome.webNavigation.onCommitted
  10. JS注入时间:等待文档。标题/文档。身体
  11. JS注入方法:appendChild
  12. JS注入方法:chrome.tabs.executeScript()
  13. JS注入代码:Link元素链接到扩展名为的css文件JS注入代码:直接执行javascript

代码示例:

清单:

{
  "manifest_version": 2,
  "name": "foo",
  "short_name": "bar",
  "description": "baz",
  "options_page": "options.html",
  "version": "2.1.1",
  "homepage_url": "http://google.com/",
  "permissions": ["storage", "*://google.com/*", "webNavigation", "tabs", "activeTab"],
  "browser_action": {
    "default_icon": "icon16.png",
    "default_title": "title",
    "default_popup": "popup.html"
  },
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },
  "content_scripts": [
    {
      "matches": ["*://google.com/*"],
      "js": ["carbonicEditionScript.js"],
      "all_frames": true,
      "run_at": "document_start"
    }
  ],
  "background": {
    "page": "popup.html"
  },
  "web_accessible_resources": ["carbonicEditionStyle.css"]
}

carbonicEditionScript.js

document.addEventListener('DOMSubtreeModified', injectStyle, false);
function injectStyle(){
    document.removeEventListener('DOMSubtreeModified', injectStyle, false);
    var style = document.createElement('style');
    style.setAttribute("id", "CarbonicEditionStyle");
    style.setAttribute("class", "CarbonicEditionStyle");
    style.setAttribute("type", "text/css");
    style.appendChild(document.createTextNode(css));
    document.getElementsByTagName("html")[0].appendChild(style);
}

carbonicEditionScript . js

document.addEventListener('DOMSubtreeModified', injectCSS, false);
function injectCSS(){
var style = document.createElement('link');
style.rel = 'stylesheet';
style.type = 'text/css';
style.href = chrome.extension.getURL('carbonicEditionStyle.css');
if(document.head){
  document.removeEventListener('DOMSubtreeModified', injectCSS, false);
  (document.head||document.documentElement).appendChild(style);
}}

background.js

chrome.webNavigation.onCommitted.addListener(function(o) {
  chrome.tabs.executeScript(o.tabId, {
        code: "var css = 'body{background-color: green !important;}'; var style = document.createElement('style'); style.setAttribute('id', 'CarbonicEditionStyle'); style.setAttribute('class', 'CarbonicEditionStyle'); style.setAttribute('type', 'text/css'); style.appendChild(document.createTextNode(css)); document.getElementsByTagName('html')[0].appendChild(style);"
  });
}, {
  url: [{hostContains: 'google.com'}]
});

有人知道发生了什么事吗?以上所有解决方案都有效,但闪烁仍然发生。

style -chrome扩展通过使用webNavigation.onCommited事件修复了闪烁,所以你应该已经能够解决这个问题了。但是,您遇到的问题可能是由于异步读取扩展包中的css代码引起的,因为您提到了web_accessible_resources。在这种情况下,将其缓存在chrome.storage.localsessionStoragelocalStorage中。或者考虑将CSS嵌入到你的内容脚本中。

关于你所发布的代码的一些[可能是多余的]注释:

  1. 不要使用DOMSubtreeModified事件,因为它a)实际上不需要-(你可以在网页被解析之前注入元素)和b)它是古老的/过时的/缓慢的/坏的。

    所以整个内容脚本可能是:

    var style = document.createElement('style');
    style.id = "CarbonicEditionStyle";
    style.className = "CarbonicEditionStyle";
    style.type = "text/css";
    style.textContent = "body{background-color: green !important;}";
    (document.body || document.head || document.documentElement).appendChild(style);
    
  2. executeScript中使用runAt: "document_start",因为默认情况下它是document_idle,通常只在DOM加载和解析时发生:

    chrome.tabs.executeScript(o.tabId, {runAt: "document_start", code: "......"});
    
  3. 考虑通过insertCSS直接注入CSS(不是真的需要消除闪烁,它不会允许你禁用注入的样式,但为了完整性):

    chrome.tabs.insertCSS(o.tabId, {runAt: "document_start", code: "body{background....."});
    
  4. "matches": ["*://google.com/*"],将不匹配www.google.com,这是默认使用的,所以应该是"matches": ["*://*.google.com/*"],
  5. google.com不是唯一的域名,还有很多国际域名。