34

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

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

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

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

4

5 回答 5

23

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

这个问题的解决方案 - 在没有 iframe 的情况下隔离样式 - 是Shadow DOM从 Chrome 25 开始)。您可以在HTML5 Rocks找到教程。有关使用 Shadow DOM 隔离样式的真实 Chrome 扩展,请参阅显示 #Anchors此处为源代码)。

于 2013-11-27T11:29:05.563 回答
14

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

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

那么,我们将何去何从?我相信今天最好的解决方案是 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

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

博文

示例源

于 2014-08-03T02:02:24.757 回答
7

要么使用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;
}
于 2015-03-03T12:39:41.163 回答
5

我最近创建了 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

于 2014-10-17T18:54:48.040 回答
2

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

马克西姆已经写了一篇关于它的文章。

于 2013-11-27T08:25:20.470 回答