Add a StyleSheet in JS and allow it to be toggled dynamically - javascript

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.)

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!

Related

is there a way to import needed jquery without effecting site jquery

is there a way programically import jquery only effecting one div and I have a second question is there a way to import a stylesheet to only effect one div to
currently for css im doing:
var l = document.createElement("link");
l.rel = "stylesheet";
l.href = "url";
document.getElementById("id").appendChild(l);
but if I'm not mistaken that effects the rest of the document.
Both is possible using a shadowRoot.
This wont work in IE, have a look at: canIUse (attachShadow)
This may be useful if you dont want (or cannot) alter the target page.
You could use your own jQuery-Version for every shadowRoot without affecting your DOM.
Working Example:
const target = document.getElementById('target');
const shadowRoot = target.attachShadow({mode: 'open'});
// Add paragraph (global style does not apply)
const paragraph = document.createElement('p');
paragraph.innerHTML = '<ul><li>I get bold but not blue</li></ul>'
shadowRoot.appendChild(paragraph);
// Add Style (applies only for shadow)
const style = document.createElement('style');
style.innerHTML = 'li { font-weight: bold; }'
shadowRoot.appendChild(style);
// Add external js (like jquery) likewise
li {
color: #00F;
}
<ul>
<li>I get blue but not bold</li>
</ul>
<div id="target"></div>

How to properly parse HTML string with style and CSS and append it to the DOM

Working on a javascript widget, I need to add some styled HTML to a page, without jQuery or document.write.
I have two javascript variables (from a JSON parsed file), widgetCode that contains HTML code and widgetStyle that contains CSS rules.
Here is the original HTML, before being modified by the javascript:
<h1 class="red">Title</h1>
<div class="target"></div>
Here is my javascript code:
document.addEventListener("DOMContentLoaded", function(event) {
var widgetCode = '<div class="red" style="text-decoration: underline;">My beautiful widget</div>',
widgetStyle = '.red{color:red;}',
parser = new DOMParser(),
parsed = parser.parseFromString(widgetCode, "text/xml"),
target = document.getElementsByClassName("target")[0],
head = document.head || document.getElementsByTagName('head')[0],
style = document.createElement('style');
// Append HTML element
target.appendChild(parsed.documentElement);
// Append CSS rules to HEAD
style.type = 'text/css';
if (style.styleSheet) style.styleSheet.cssText = widgetStyle;
else style.appendChild(document.createTextNode(widgetStyle));
head.appendChild(style);
});
Here are both on CodePen: http://codepen.io/anon/pen/yYavBP
The problem is, after the HTML and JavaScript were inserted, the widget content properly shows up in the page, but the class and style attributes seems to be ignored (although the h1 original tag, that also has the red class, turns red as it should).
What am I missing here ? Why does the browser seem to ignore style ? Do I have to refresh it somehow ?
Edit: Actually, I just noticed that my code is working on Firefox and Safari. The problem seems to be Google Chrome (for Mac at least) specific.

How can I get the css file text via javascript?

If I have a style tag on my page with css in it and I write the following javascript I will get the css text of all style tags.
//compatibility: all
$("style").each(function () {
alert($(this).text());
});
I want to get the same text from all link element css files, like the following script.
//compatibility: IE Only
$("link").each(function(){
alert(this.sheet.cssText);
});
Is there a cross modern browser friendly version of the above script?
Another way to access a CSS rule without actually accessing the stylesheet is to create an element, apply a rule to it and then access its properties with jQuery. Something like this:
var NewElement = $('.SomeClass');
var TheHeight = NewElement.prop('height');
Not sure if this would help but it's an idea. What are you trying to do anyway?
Edit:
var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules;
rules[0].style.color = 'red';
This is from the answer here I added a jsFiddle Note that you must select the correct stylesheet index.
The only possible solution is to use AJAX:
$("link").each(function(){
$.get(this.href, function(css){
alert(css);
});
});
Or you can use document.styleSheets
You can iterate over the style sheets:
var styleSheets = document.styleSheets;
for(var i = 0; i < styleSheets.length; i++){
alert(styleSheets[i].cssRules)
}

Clear all Javascript-applied styles

Let's say we have this
var e = document.getElementById("someElement");
e.style.borderColor = "gold";
e.style.background = "yellow";
e.style.padding = "5px";
// more style modifications via javascript
There may have been other styles set inline or in an external CSS file.
Is there a method to clear all Javascript-applied styles? Like e.style.* = inherit or e.removeJavaScriptAppliedStyles().
Here you go:
<div>hi</div>
<div>hi</div>
<div>hi</div>
<div>hi</div>
<div id="click">click</div>
//
$("div").css("color", "red");
$("#click").click(function () {
$("*").removeAttr("style");
});
Keep in mind James Bruckner's warning that this kills all inline styles that were hardcoded into the original HTML. Since Javascript changes CSS by manipulating the style attribute, there's no real way to distinguish what was original, and what was set programmatically. (EDIT. See the comments below.)
http://jsfiddle.net/FASvA/
As an option you could store initial style attribute (e._originalStyle = e.style.cssText) in a private property and restore it when you'll need to drop all styles applied via javascript (e.style.cssText = e._originalStyle)

Javascript: how to change the style of all links in a page?

I'm trying to change the style of all links in a page via Javascript.
I tried both these but no success:
document.links.style.cursor = "wait";
document.getElementsByTagName(a).style.cursor = "wait";
What am I doing wrong?
var allLinks = document.links || document.getElementsByTagName('a');
for(var n=0;n<allLinks ;n++)
document.allLinks [n].style.cursor = "wait";
The document.links is an array of elements, so document.links.style.cursor = "wait" equals to [link1,link2].links.style.cursor = "wait".
And about document.getElementsByTagName(a): the syntax is wrong, you forgot the quotes. The correct is document.getElementsByTagName("a")
var style = document.createElement("style");
document.head.appendChild(style);
try {
style.innerHTML = "a { cursor: wait; }";
}
catch (_ie) {
style.styleSheet.cssText = "a { cursor: wait; }";
}
Of course alternatively you could plan in advance for the eventuality of needing your links to change, and pre-load a static style sheet like this:
.wait-links a { cursor: wait; }
Then whenever you add the class "wait-links" to the <body> (or any container you like), the <a> tags will all be affected. That'd be much more efficient than iterating over the elements changing their individual styles (probably), though if there aren't many links it probably doesn't matter.
Using a style sheet for such things is, in my opinion, a much better practice. In this case, in fact, it'd be better not to call the class "wait-links", but rather use some word or words that describe what's actually happening. Then your JavaScript code just establishes the situation by adding (or removing) the class, and the style sheet controls the appearance changes. If you decide that in some cases the situation calls for the links to change color or become invisible, you can do that without having to root around in your scripts looking for style changes.
Using the JQuery and the ".css" method removes the need to have a for loop and simplifies the code.
$('a').css("cursor","wait");

Categories