在内容脚本加载到Chrome扩展程序之前,如何避免出现样式错误的内容? [英] How to avoid a flash of misstyled content before content scripts load in a chrome extension?

查看:149
本文介绍了在内容脚本加载到Chrome扩展程序之前,如何避免出现样式错误的内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建我的第一个Chrome浏览器扩展程序。它应使用内容脚本在指定域的每个页面上操作DOM。最初,我仅使用要处理的页面中已经存在的选择器在 style.css 中创建了样式规则-此方法按预期工作。

I'm trying to create my first Chrome browser extension. It should use content scripts to manipulate the DOM on every page of the specified domain. Initially I created a style rule in style.css using only selectors that already existed in the page I was manipulating—this method worked as expected.

然后我决定通过添加选项来扩展功能,从而允许用户从与3种不同样式规则相关的3种状态中进行选择。我添加了 scripts.js 来设置基于所选选项的类,该选项将用作选择器以应用适当的样式规则。问题在于,现在我必须等待从chrome存储读取状态,然后才能应用自定义类,这意味着在我的样式生效之前,页面上会闪烁默认样式。

I then decided to extend the functionality by adding options, allowing the user to choose from 3 states which relate to 3 different style rules. I added scripts.js to set a class based on the chosen option that I would use as a selector to apply the appropriate style rule. The problem is that now I have to wait for the state to be read from chrome storage before my custom class is applied which means there's a flash of the default styles on the page before my styles take effect.

应该使用什么方法来防止样式加载之前的延迟?

What method should I use to prevent the delay before my styles load?

manifest.json (部分)

"content_scripts": [
  {
    "js": [ "scripts.js" ],
    "css": [ "style.css" ],
    "matches": [ "https://example.com/*" ]
  }
]

scripts.js

chrome.storage.sync.get("state", function (obj) {
  var elem = document.getElementById('targetId');

  if (obj.state === 'targetState') {
    elem.className += ' myClass';
  }

});

style.css

.myClass {
  /* do something */
}


推荐答案

时尚的chrome扩展名使用以下步骤解决了此问题:

Stylish chrome extension solved this problem using the following steps:


  1. 将状态缓存在后台页面中脚本变量并将其消息发送回内容脚本,该脚本将在开始时要求提供数据。

  2. (可选)从 chrome.webNavigation.onCommitted 发生在页面开始加载之前,有时甚至在内容脚本运行之前,因此此方法只是附加方法测量。不过,您可以将其设为唯一的方法,例如使用setlival在后台页面脚本中。

  3. 使用 persistent:true 背景页面。可以说,在这种通信方案中,这是唯一避免非持久事件页面可靠地避免 FOUC 的方法需要一些时间来加载。

  4. document_start

    执行内容脚本时,文档为空,没有头,没有正文。此时,时尚扩展(其功能是样式注入)仅在< html> 下直接添加一个样式元素。

  1. Cache the state in a background page script variable and message it back to the content script which asks for the data on its start.
  2. Optionally send a message to the content script from chrome.webNavigation.onCommitted which occurs before the page started loading, sometimes even before a content script runs so this method is just an additional measure. You can make it the sole method, though, by sending the same message several times using e.g. setInterval in the background page script.
  3. Use a "persistent": true background page. Arguably, it's the only method to avoid FOUC reliably in this communication scenario as non-persistent event pages need some time to load.
  4. Declare the content script to be injected at "document_start".
    When the content script executes the document is empty, no head, no body. At this point Stylish extension, its function being style injection, simply adds a style element directly under <html>.

在您的情况下,还需要执行其他步骤:

In your case an additional step is needed:


  1. 使用MutationObserver进行处理页面加载时的页面(示例性能信息)。

  1. Use MutationObserver to process the page as it's being loaded (example, performance info).

manifest.json:

manifest.json:

"background": {
    "scripts": ["background.js"]
},
"content_scripts": [
    {
        "js": ["contents.js"]
        "matches": ["<all_urls>"],
        "run_at": "document_start",
        "all_frames": true,
    }
],

内容脚本:

var gotData = false;

chrome.runtime.sendMessage({action: 'whatDo'}, doSomething);

chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
    if (msg.action == 'doSomething') {
        doSomething(msg);
    }
});

function doSomething(msg) {
    if (gotData || !msg || !msg.data)
        return;

    gotData = true;

    new MutationObserver(onMutation).observe(document, {
        childList: true, // report added/removed nodes
        subtree: true,   // observe any descendant elements
    });

    function onMutation(mutations, observer) {
        // use the insanely fast getElementById instead of enumeration of all added nodes
        var elem = document.getElementById('targetId');
        if (!elem)
            return;
        // do something with elem
        .............
        // disconnect the observer if no longer needed
        observer.disconnect();
    }
}

背景页面脚本:

var state;

chrome.storage.sync.get({state: true}, function(data) {
    state = data.state;
});

chrome.storage.onChanged.addListener(function(changes, namespace) {
    if (namespace == 'sync' && 'state' in changes) {
        state = changes.state.newValue;
    }
});

chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
    if (msg.action == 'whatDo') {
        sendResponse({action: 'doSomething', data: state});
    }
});

chrome.webNavigation.onCommitted.addListener(function(navDetails) {
    chrome.tabs.sendMessage(
        navDetails.tabId,
        {action: 'doSomething', data: state},
        {frameId: navDetails.frameId}
    );
});

重复消息传递,一个简单的示例,它不检查消息是否已处理:

Repeated messaging, a simple example that doesn't check if the message was processed:

chrome.webNavigation.onCommitted.addListener(function(navDetails) {
    var repetitions = 10;
    var delayMs = 10;
    send();

    function send() {
        chrome.tabs.sendMessage(
            navDetails.tabId,
            {action: 'doSomething', data: state},
            {frameId: navDetails.frameId}
        );
        if (--repetitions)
            setTimeout(send, delayMs);
    }
});

这篇关于在内容脚本加载到Chrome扩展程序之前,如何避免出现样式错误的内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆