Jon
Jon

Reputation: 2399

Chrome Extension - Prevent CSS From Being Over Written

I am writing a Chrome Extension where a small panel appears on top of the existing website. When I go to certain websites, I notice that the CSS of my panel has been over-written by the website's CSS. I am currently using Eric Meyer's CSS Reset but it does not seem to be doing the trick. Is there something else I can do?

Upvotes: 10

Views: 4785

Answers (5)

Like other answers, I got it working injecting the styles by myself. But today (v3), I had to do it like this:

manifest.json

{
  ...
  "content_scripts": [
    {
      "js": ["main.js"],
      "matches": [...]
    }
  ],
  "web_accessible_resources": [{
    "resources": ["overrided-styles.css"],
    "matches": [...]
  }]
}

main.js

const link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
link.setAttribute('href', chrome.runtime.getURL('overrided-styles.css'));
document.querySelector('head').appendChild(link);

Upvotes: 0

jCyCle
jCyCle

Reputation: 466

An extension that I just wrote ran into similar problems. I've made most of them disappear, but not all of them. I think that I know why, but I haven't gotten around to fixing the exceptions (this is just a school assignment as of now).

Here is what I found: when a stylesheet is injected through the extension manifest or by the background page, it is treated as a user stylesheet, giving it cascade priority over the default browser stylesheet only. Adding !important directives to your rules will not help, at least in my experience. User stylesheets (added by an extension or manually) can contain !important directives, but they are not honoured by Chrome for some reason -- just check how they show up in the Chrome DevTools, without !important. Adding id attributes won't help either, as specificity will only trump where priority is equal otherwise.

What does work for me:

var ninjaCSS = document.createElement("link");
ninjaCSS.setAttribute("rel", "stylesheet");
ninjaCSS.setAttribute("type", "text/css");
ninjaCSS.setAttribute("href", chrome.extension.getURL('ninja.css'));
document.getElementById("head").appendChild(ninjaCSS);

This code is included in a JavaScript file that is listed in the manifest as a content script, and should run at document load. The CSS file is not listed in the manifest, but is included in the extension folder. Now the stylesheet is on an equal footing with the the other author stylesheets.

Of course, that is just the beginning. You can now give all elements in your panel an id attribute (you probably already have). Whether you use a style reset or not is up to you. But you will have to make sure that your styles specify every single rule that a stylesheet in the wild might try to manipulate. If you do not plan to change a rule from its default, you must still specify that default value. Even if the default value is "none";

Finally, you must bravely ignore all warnings that the !important directive is best used sparingly. Quite the opposite applies here. When you add !important to every one of your style rules, it will be as if you had not used it at all as far as your panel's cascade is concerned. On the other hand, you will now be the boss of your panel. Trust me, somebody is going to tack an !important directive on, say, their button:hover background-image rule. Leading your well-crafted buttons to inexplicably morph into concert images of a 1985 bon jovi concert -- but only when the mouse is hovering, so no worries, right?

Upvotes: 1

Devin Rhode
Devin Rhode

Reputation: 25377

Here's a nifty 'hack' with iframes, where you don't actually instantiate an iframe:

  1. Append an iframe to the DOM, this will be a container for your do dad
  2. Walk into the iframe and add your HTML code to the innerHTML of the body

It looks like this:

var iframe = document.createElement('iframe');
document.documentElement.appendChild(iframe); //fastest way to append to DOM: http://jsperf.com/insertbefore-vs-appendchild/2
iframe.contentDocument.body.innerHTML = '<a href="yomama.com">Normal link!!</a>';

Upvotes: 10

gekko
gekko

Reputation: 1

appendChild solutions works for me (Devin G Rhode and jCyCle answers). But I noticed these solutions just add the attribute xmlns="http://www.w3.org/1999/xhtml". So I tested my code just by adding this xmlns attribute to my link tag (directly, not using JS) and it works too, don't know why.

Failing:

<link rel="stylesheet" type="text/css" href="filesystem:chrome-extension://................/temporary/Content/Styles/style.css" />

Working:

<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" type="text/css" href="filesystem:chrome-extension://.............../temporary/Content/Styles/style.css" />

Upvotes: 0

nzifnab
nzifnab

Reputation: 16120

I'm not familiar with Chrome extensions themselves. But you could try scoping your panel within an 'id':

<div id='my-panel'>
  PANEL GOES HERE
</div>

And then in the CSS just have #my-panel as the first selector for all of your css. Take the reset css and add the #my-panel identifier to each element defined there too. Might be tedious... but would ensure you're resetting all of your elements, and virtually guarantee that they'll be reset at a higher priority than anything the website might be defining.

Upvotes: 1

Related Questions