user314159 user314159 - 4 months ago 20x
Javascript Question

How to dynamically modify CSS rule set (e.g. with a class selector) from JavaScript within Firefox Add-on using XUL, SDK or WebExtensions techniques?

How to dynamically modify CSS rule set (e.g. with a class selector) from JavaScript within Firefox Add-on using XUL, SDK or WebExtensions techniques? Trying to support Firefox 29.0b1 through 49.0a1.

Problem I'd like to solve (from within a XUL document within a Firefox add-on, which is sometimes different than a web page):

(has this line at the top)

<?xml-stylesheet href="chrome://{GUID}/skin/text.css" type="text/css"?>

(relevant class within the file)

.myTextClass {
max-width: 800px;


// Modify the class selector rule set to add another rule,
// i.e. font-family: DejaVu Sans Mono

// Later on, after user input, change or remove this font in this one class

This may be in-part a duplicate of #20927075, which didn't have satisfactory answers, but I am additionally interested in SDK or WebExtensions techniques as well, to ensure the feature I want to add to an existing XUL based add-on can be implemented with the newer APIs.

I want to implement a rudimentary "font selector" UI feature, and have it apply the new font to the class. I have a simple CSS rule set with a class selector which defines one class with one rule (for now, but may have other rule sets manually/statically added), to which I would like to add another rule, and have the add-on's interface updated.

The options I am aware of, but which all seem too clumsy.

1) document.getElementById, set
attribute manually. I do this for the demo area, that's fine.

2) Similar as above, but getElementByClassName. However, this is NOT USING A CLASS! This is using a class just to find nodes, to which I change each element's attribute, either style or class. This seems rather slow. I could create a massive CSS file with a class every possible font, then change class attribute, but this is stupid. Worse than just changing style attribute.

3) There's the style sheet service, but I don't know if it's still in existence or will be obsoleted or removed (it's XPCOM based?), it's not part of Services.jsm, and it is a very blunt instrument that can merely load or unload an entire style sheet, when I merely want to modify one rule from one class definition.

4) There's jQuery, but, no. Just no. Do not even bother suggesting that. Too much overhead for a small add-on, and no idea if the technique works within XUL interface code.

5) There's things like document.styleSheets which seems right but doesn't get what I want (unless I am mistaken?). Everything seems read-only. More below.

6) There's manual clobbering of entire style sheets by fiddling with document.getElementsByTagName('head'), which again clobbers an entire css sheet rather than one rule of one class.

If there is really no other option, I may have to use method 2 above. But I was hoping for an option that allows fine grained control over CSS from within a XUL using JavaScript.

What I was trying, to understand how to do this:

for ( i = 0; i < document.styleSheets.length; i++ ) {
styleSheet = document.styleSheets[ i ];

console.log( 'style sheet: ' + i + ' ' + styleSheet.href );

if ( styleSheet.href === 'chrome://{GUID}/skin/text.css' ) {
for ( j = 0; j < styleSheet.cssRules.length; j++ ) {
styleRule = styleSheet.cssRules[ j ];
console.log( 'style sheet: ' + i + ' ' + 'styleRule: ' + styleRule.cssText );

During this edit, I now get the code to display the rule which defines the class, whereas last night I was getting an invalid property error. I had my terminology mixed up, thinking the rules were the line-items inside the class. But anyways, from that point, I was not sure how to proceed.

The above code (in
which is referenced from a
tag) outputs console.log similar to this (in Firefox 29.0b1):

"style sheet: 0 chrome://git-addonbar/skin/addonbar.css" file.js:189
09:49:47.101 "style sheet: 1 chrome://browser/content/browser.css" file.js:189
09:49:47.101 "style sheet: 2 chrome://browser/content/places/places.css" file.js:189
09:49:47.101 "style sheet: 3 chrome://browser/skin/devtools/common.css" file.js:189
09:49:47.101 "style sheet: 4 chrome://browser/skin/customizableui/panelUIOverlay.css" file.js:189
09:49:47.101 "style sheet: 5 chrome://browser/skin/browser.css" file.js:189
09:49:47.101 "style sheet: 6 chrome://browser/skin/browser-lightweightTheme.css" file.js:189
09:49:47.101 "style sheet: 7 chrome://global/skin/global.css" file.js:189
09:49:47.101 "style sheet: 8 chrome://{GUID}/skin/text.css" file.js:189
09:49:47.102 "style sheet: 8 styleRule: .myTextClass { max-width: 800px; }" file.js:194
09:49:47.102 "style sheet: 9 null" file.js:189


5) There's things like document.styleSheets which doesn't seem to get what I want (unless I am mistaken?). Everything seems read-only.

This is the correct option. While the styleSheets property is read-only (meaning you can't assign like this: document.styleSheets = val), the stylesheet object you get does allow modifications.

Since you only care about one modern browser this is easier than Changing a CSS rule-set from Javascript in a cross-browser way. A minimal example (which you'll have to extend to find the stylesheet and the rule you care about - the question linked above has a minimal example of doing that).

function changeCSS() {
  let myClassRule = document.styleSheets[0].cssRules[0];
  if ( == "red") = "blue";
  else = "red";
.my-class {
  color: red;
<p class="my-class">First paragraph</p>
<p class="my-class">Second paragraph</p>
<button onclick="changeCSS()">Change the stylesheet!</button>