如何在Google Chrome扩展程序中真正隔离样式表?

时间:2012-10-08 13:56:22

标签: css google-chrome-extension isolation

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

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

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

顺便说一句:我设置我的清单来加载“document_end”中的所有内容,但我发现它并没有应用于每次DOM准备就绪时每次加载的样式表。

5 个答案:

答案 0 :(得分:21)

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

此问题的解决方案 - 没有iframe的样式的隔离 - 是Shadow DOMsince Chrome 25)。您可以在HTML5 Rocks找到教程。对于使用Shadow DOM隔离样式的真实Chrome扩展程序,请参阅Display #Anchorssource code here)。

答案 1 :(得分:10)

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

首先,Rob W的回答是正确的。 Shadow DOM是解决此问题的正确方法。但是,在我的情况下,我不仅需要CSS隔离,还需要JavaScript事件。例如,如果用户单击位于隔离HTML中的按钮会发生什么?只有Shadow DOM才会变得非常丑陋,但我们还有另一个Web组件技术,即自定义元素。除了撰写本文时,Chrome中存在一个错误,它会阻止Chrome扩展中的自定义元素。请参阅我的问题herehere以及the bug here

那么我们离开了哪里?我相信今天最好的解决方案是IFrames,这就是我的选择。 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://anderspitman.net/blog/chrome-extension-content-script-stylesheet-isolation/

如果我的博客发生故障,这里是原帖的来源:

Blog post

Example source

答案 2 :(得分:6)

使用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;
}

答案 3 :(得分:5)

我最近创建了Boundary,一个CSS + JS库来解决这样的问题。边界创建的元素与现有网页的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

答案 4 :(得分:2)

使用iframe。这是一种解决方法,但工作正常。

Maxime写了article on it