0

I know how to use JS to add a stylesheet to an HTML document:

// given CSS as text, add a <style> to the document
function addStyle(css) {
  var style = document.createElement("style");
  style.type = "text/css";
  style.appendChild(document.createTextNode(css));
  document.head.appendChild(style);
}

How do I create a checkbox that can toggle this (and only this) stylesheet on and off?

This answer gives hints about how to manipulate stylesheets in JS, but there does not appear to be a way of locating a specific stylesheet without looping through document.styleSheets and matching against document.styleSheets[i].cssRules[j].cssText, which seems unwieldy.

Is there a better way to do this, ideally without jQuery, than a double loop?

(My particular use case is for a userscript (e.g. Greasemonkey), though I avoid GM_addStyle for portability. All this really means is that I don't have direct control over the HTML; I'm modifying other sites.)

Adam Katz
  • 14,455
  • 5
  • 68
  • 83

1 Answers1

1

In writing this question, I figured out the answer, though it still leaves open the question of how to manipulate preexisting stylesheets without too many loops.

Basically, while I can't use getElementById() or querySelector() to find a style element by its id attribute (because it's not in the body), I can save the object itself when I create it:

// given CSS as text, add a <style> to the document and return it
function addStyle(css) {
  var style = document.createElement("style");
  style.type = "text/css";
  style.appendChild(document.createTextNode(css));
  document.head.appendChild(style);
  return style;
}

var toggler = addStyle(`
  tr.informational { display:none; }
`);

// create and insert the toggling checkbox
var checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.checked = true;
checkbox.onchange = function() { toggler.disabled = ! toggler.disabled };
var label = document.createElement("label"); // <label> allows clicking on text
label.appendChild(checkbox);
label.appendChild(document.createTextNode("Hide informational rows"));

document.getElementById("buttons").appendChild(label); // add to button panel

I had previously thought that Greasemonkey's security prevented accessing its objects after it finishes loading. This would have meant the checkbox.onchange() line wouldn't work. My original code was therefore quite ugly, finding toggler_index by looping over document.styleSheets[] to find my CSS and then constructing independent JS within JS by hard-coding that index:

checkbox.setAttribute("onchange", `
  var toggler = document.styleSheets[${toggler_index}];
  toggler.disabled = ! toggler.disabled;
`);

Thanks to StackOverflow for forcing me to question my assumptions when simplifying my question for posting here!

Adam Katz
  • 14,455
  • 5
  • 68
  • 83