¿Cómo aislar realmente las hojas de estilo en la extensión de Google Chrome?

Escribí la extensión Google Chrome, el cuadro de diálogo emergente con el campo autocompletar y su propio estilo, pero hay algunos sitios en los que mi CSS se rompió por completo y no se ve muy bien.

Sé sobre el aislamiento de estilos con iFrames, pero en la extensión de Google Chrome no hay forma de aislar mi HTML y CSS de esta manera. Otro método es envolver todas mis cosas en el div separado con su propia identificación y estilos relativos para ese ID, y lo hago, pero parece que no funciona en algunos sitios con sobrecarga de estilo de tags duras o directivas “! Important” en el código CSS

Así que quiero saber si hay alguna manera de aislar realmente mis estilos de manera conveniente o si es mi mal carma sobrecargar cada pequeña propiedad de CSS para corregir uno u otro error de estilo en cada sitio.

Por cierto: configuré mi manifiesto para cargar todas las cosas en “document_end”, pero veo que no se aplica a las hojas de estilo que se cargan cada vez antes de que cualquier DOM esté listo.

En el momento de formular la pregunta, su única opción era utilizar iframes o hojas de estilo con una especificidad muy alta y establecer explícitamente todas las propiedades que podrían afectar a los estilos. El último método es muy engorroso, porque siempre habrá alguna propiedad que usted pase por alto. En consecuencia, el único método utilizable para aislar hojas de estilo era usar iframes.

La solución a este problema (aislamiento de estilos sin iframes) es Shadow DOM ( desde Chrome 25 ). Puede encontrar un tutorial en HTML5 Rocks . Para obtener una extensión de Chrome del mundo real que use Shadow DOM para aislar estilos, consulte Display #Anchors ( código fuente aquí ).

Como recientemente he pasado por el guante de este problema, quiero compartir algo de información que creo que es valioso.

Primero, la respuesta de Rob W es correcta. Shadow DOM es la solución correcta para este problema. Sin embargo, en mi caso no solo necesitaba el aislamiento de CSS, también necesitaba eventos de JavaScript. Por ejemplo, ¿qué sucede si el usuario hace clic en un botón que vive dentro del HTML aislado? Esto se vuelve realmente feo solo con Shadow DOM, pero tenemos otra tecnología de componentes web, elementos personalizados, para el rescate. Excepto que a partir de este escrito hay un error en Chrome que impide el elemento personalizado en las extensiones de Chrome. Vea mis preguntas aquí y aquí y el error aquí .

Entonces, ¿dónde nos deja eso? Creo que la mejor solución hoy es IFrames, que es a lo que recurrí. El artículo shahalpk linked es excelente, pero solo describe parte del proceso. Así es como lo hice:

Primero, cree un archivo html y un archivo js para su widget aislado. Todo dentro de estos archivos se ejecutará en un entorno aislado en un iframe. Asegúrese de obtener su archivo js del archivo html.

 //iframe.js var button = document.querySelector('.my-button'); button.addEventListener('click', function() { // do useful things }); //iframe.html    

A continuación, dentro de su secuencia de comandos de contenido, cree un elemento iframe en javascript. Debes hacerlo en JavaScript porque debes usar chrome.extension.getURL para obtener tu archivo iframe html:

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

Y eso es.

Una cosa a tener en cuenta: si necesita comunicarse entre el iframe y el rest del script de contenido, necesita chrome.runtime.sendMessage () a la página de fondo, y luego chrome.tabs.sendMessage desde la página de fondo. a la pestaña. No pueden comunicarse directamente.

EDITAR: Escribí una publicación de blog que detallaba todo lo que aprendí a través de mi proceso, incluida una extensión completa de ejemplo de Chrome y muchos enlaces a información diferente:

https://anderspitman.net/blog/chrome-extension-content-script-stylesheet-isolation/

En caso de que mi blog se caiga, aquí están las fonts de la publicación original:

Entrada en el blog

Fuente de ejemplo

O usa all

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

o usa Shadow DOM

Biblioteca

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

Uso

 var myWidget = new Widget(); myWidget.inner.innerHTML = '

myWidget

';

Puede acceder a los contenidos del widget a través de myWidget.inner y el exterior a través de myWidget.outer .

Estilos

 /* * 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; } 

Recientemente creé Boundary, una biblioteca CSS + JS para resolver problemas como este. Boundary crea elementos que están completamente separados del CSS de la página web existente.

Tome la creación de un diálogo, por ejemplo. Después de instalar Boundary, puede hacer esto en su script de contenido

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

Los elementos dentro de #yourDialogID no se verán afectados por la página web existente. Y la función find () devuelve un elemento jQuery DOM regular para que pueda hacer lo que quiera con él.

Espero que esto ayude. Por favor avísame si tienes alguna pregunta.

https://github.com/liviavinci/Boundary

Use iframes. Es una solución, pero funciona bien.

Maxime ha escrito un artículo sobre eso .