【问题标题】:How to really isolate stylesheets in the Google Chrome extension?如何真正隔离 Google Chrome 扩展中的样式表?
【发布时间】:2021-12-04 17:15:17
【问题描述】:

我编写了一个谷歌浏览器扩展程序,它会弹出一个带有自动完成字段的对话框,它有自己的风格,但是在某些网站上我的 CSS 完全被破坏了,看起来不太好。

我知道使用 iFrame 隔离样式,但在 Google Chrome 扩展程序中,无法以这种方式隔离我的 HTML 和 CSS。另一种方法是将我所有的东西包装到一个单独的 div 中,并使用它自己的 id 和该 id 的相对样式,我这样做了,但它似乎不适用于某些具有“硬”标签样式重载或“!重要的”指令在 CSS 代码中。

所以,我想知道有没有什么方法可以真正以方便的方式隔离我的样式,或者重载每个小的 CSS 属性来修复每个站点的一个或另一个样式问题是我的坏事?

顺便说一句:我将清单设置为在“document_end”加载所有内容,但我发现它并没有应用于样式表,而样式表每次都在 DOM 准备好时加载。

【问题讨论】:

  • 你试过these methods吗?两者的结合应该足够了。确保您的选择器足够具体。
  • 谢谢罗伯!是的,我试过这个,但它似乎不适用于每个站点。我知道即使我看到“计算样式”(在 GC DevTools 中)显示的所有页面都是我在 CSS 中设置的,例如“list-style: none !important;”,它仍然会继续向我显示带有“光盘”标记的列表。而且,是的 - 我的样式在清单中设置,就像在你的建议中一样,它是在创建 DOM 后通过嵌入到头部动态加载的。也许还有其他方法可以隔离 Google Chrome 扩展中的样式?我的意思是清单中的一些秘密“禁用继承:开启”选项或类似的东西?
  • 我最后的建议怎么样?增加选择器的specifity。如果您控制要设置样式的元素,则添加 style="prop:val!important" 将具有最高优先级 - 例如参见 jsfiddle.net/rytkL
  • 在此处查看我对类似问题的回答:stackoverflow.com/questions/10608924/…。有几种方法,但我详细介绍的一种可能是最简单和最可靠的...... :)

标签: css google-chrome-extension isolation


【解决方案1】:

使用 iframe。这是一种解决方法,但效果很好。

Maxime 写了一封article on it

【讨论】:

    【解决方案2】:

    在提出问题时,您唯一的选择是使用 iframe 或 specificity 非常高的样式表,并明确设置所有可能影响样式的属性。最后一种方法很麻烦,因为总会有一些属性被你忽略。因此,隔离样式表的唯一可用方法是使用 iframe。

    这个问题的解决方案 - 没有 iframe 的样式隔离 - 是 Shadow DOM (since Chrome 25)。您可以在HTML5 Rocks 找到教程。有关使用 Shadow DOM 隔离样式的真实 Chrome 扩展程序,请参阅Display #Anchors (source code here)。

    【讨论】:

    • Display #Anchors 扩展非常方便(例如,用于链接到 Chrome 扩展文档的特定部分;))
    • 也使用 Sass,例如:#myid { .all-my-extension-styles ... } 可能会解决这个问题?
    • @rob-w 你的答案似乎有点复杂。你能给出简化代码来注入一个元素并在其他元素的顶部覆盖它吗?
    • 我能够创建一个 shadowDOM 元素,但是一旦加载,它就会被页面覆盖。例如,在 Medium 编辑器中,它会显示一秒钟的阴影元素并隐藏。我能够在开发工具中检查元素。
    【解决方案3】:

    由于我最近经历了这个问题的挑战,我想分享一些我认为有价值的信息。

    首先,Rob W 的答案是正确的。 Shadow DOM 是解决这个问题的正确方法。然而,就我而言,我不仅需要 CSS 隔离,还需要 JavaScript 事件。例如,如果用户单击位于隔离 HTML 中的按钮会发生什么?仅使用 Shadow DOM 就变得非常丑陋,但我们有另一种 Web 组件技术,自定义元素,可以救援。除了在撰写本文时,chrome 中有一个错误会阻止 chrome 扩展中的自定义元素。请参阅我的问题 hereherethe bug here

    那么这会给我们带来什么影响呢?我相信今天最好的解决方案是 IFrame,这就是我所采用的。 shahalpk 链接的文章很棒,但它仅描述了该过程的一部分。我是这样做的:

    首先,为您的独立小部件创建一个 html 文件和 js 文件。这些文件中的所有内容都将在 iframe 中的隔离环境中运行。请务必从 html 文件中获取您的 js 文件。

    //iframe.js
    var button = document.querySelector('.my-button');
    button.addEventListener('click', function() {
        // do useful things
    });
    
    //iframe.html
    <style>
    /* css */
    </style>
    <button class='my-button'>Hi there</button>
    <script src='iframe.js'></script> 
    

    接下来,在您的内容脚本中,在 javascript 中创建一个 iframe 元素。您需要在 javascript 中执行此操作,因为您必须使用 chrome.extension.getURL 才能获取您的 iframe html 文件:

    var iframe = document.createElement('iframe');
    iframe.src = chrome.extension.getURL("iframe.html");
    document.body.appendChild(iframe);
    

    就是这样。

    要记住一点:如果需要在 iframe 和其余内容脚本之间进行通信,则需要 chrome.runtime.sendMessage() 到后台页面,然后 chrome.tabs.sendMessage 从背景页回到选项卡。他们不能直接交流。

    编辑:我写了一篇博文,详细介绍了我在这个过程中学到的一切,包括一个完整的示例 chrome 扩展和许多指向不同信息的链接:

    https://apitman.com/3/#chrome-extension-content-script-stylesheet-isolation

    如果我的博客出现故障,这里是原始帖子的来源:

    Blog post

    Example source

    【讨论】:

    • BTW 如该博客文章中所述,您可能需要查看我的 chromeps 库来处理消息传递。这个blog post 解释了它
    【解决方案4】:

    我最近创建了 Boundary,一个 CSS+JS 库来解决这样的问题。 Boundary 创建的元素与现有网页的 CSS 完全分离。

    以创建对话框为例。安装 Boundary 后,您可以在内容脚本中执行此操作

    var dialog = Boundary.createBox("yourDialogID", "yourDialogClassName");
    
    Boundary.loadBoxCSS("#yourDialogID", "style-for-elems-in-dialog.css");
    
    Boundary.appendToBox(
        "#yourDialogID",
        "<button id='submit_button'>submit</button>"
    );
    
    Boundary.find("#submit_button").click(function() {
      // some js after button is clicked.
    });
    

    #yourDialogID 中的元素不会受到现有网页的影响。 find() 函数返回一个常规的 jQuery DOM 元素,所以你可以用它做任何你想做的事情。

    希望这会有所帮助。如果您有任何问题,请告诉我。

    https://github.com/liviavinci/Boundary

    【讨论】:

    • 好!现在这是一个更好的答案。 (虽然要去掉sn-p标签,但没用)
    • @Xan 这是我第一次在 stackoverflow 上发布答案,非常感谢您的指导!
    • 这对用户事件响应良好吗?
    【解决方案5】:

    要么使用all

    .some-selector {
        all: initial;
    }
    
    .some-selector * {
        all: unset;
    }
    

    或使用 Shadow DOM

    图书馆

    function Widget(nodeName, appendTo){
      this.outer = document.createElement(nodeName || 'DIV');
      this.outer.className = 'extension-widget-' + chrome.runtime.id;
      this.inner = this.outer.createShadowRoot();
      (appendTo || document.body).appendChild(this.outer);
    }
    
    Widget.prototype.show = function(){
      this.outer.style.display = 'block';
      return this;
    };
    
    Widget.prototype.hide = function(){
      this.outer.style.display = 'none';
      return this;
    };
    

    用法

    var myWidget = new Widget();
    myWidget.inner.innerHTML = '<h1>myWidget</h1>';
    

    您可以通过myWidget.inner 和外部通过myWidget.outer 访问小部件内容。

    样式

    /* 
     * Reset Widget Wrapper Element 
     */
    .extension-widget-__MSG_@@extension_id__ {
      background: none;
      border: none;
      bottom: auto;
      box-shadow: none;
      color: black;
      cursor: auto;
      display: inline;
      float: none;
      font-family : "Helvetica Neue", "Helvetica", "Arial", sans-serif;
      font-size: inherit;
      font-style: normal;
      font-variant: normal;
      font-weight: normal;
      height: auto;
      left: auto;
      letter-spacing: 0;
      line-height: 100%;
      margin: 0;
      max-height: none;
      max-width: none;
      min-height: 0;
      min-width: 0;
      opacity: 1;
      padding: 0;
      position: static;
      right: auto;
      text-align: left;
      text-decoration: none;
      text-indent: 0;
      text-shadow: none;
      text-transform: none;
      top: auto;
      vertical-align: baseline;
      white-space: normal;
      width: auto;
      z-index: 2147483648;
    }
    
    /* 
     * Add your own styles here 
     * but always prefix them with:
     * 
     *   .extension-widget-__MSG_@@extension_id__ 
     *   
     */
    
    .extension-widget-__MSG_@@extension_id__{
      position: fixed;
      top: 100px;
      margin: 0 auto;
      left: 0;
      right: 0;
      width: 500px;
    }
    
    .extension-widget-__MSG_@@extension_id__::shadow h1 {
      display: block;
      margin: 0 auto;
      padding: 20px;
      background-color: yellow;
      border: 10px solid green;
      font-size: 20px;
      text-align: center;
    }
    

    【讨论】:

    • Shadow DOM 在事件处理方面非常棘手。我正在尝试使用 React + Shadow DOM 来隔离扩展,但仍然无法将事件处理程序绑定到内部 DOM。
    • 我也做了同样的事情。最后我意识到 react 和 shadow dom 不能很好地协同工作。在封装方面,好的旧 iframe 仍然是最佳选择。
    • 很高兴听到有人和我在一起。但似乎如果您使用的是 iframe,主机和 iframe 内容之间的消息传递可能会非常困难。你是怎么做到的?
    猜你喜欢
    • 1970-01-01
    相关资源
    最近更新 更多