Adding css global variables using javascript - css

First of all if you read this question and I did have any misunderstandings of how css works please let me know
I'm trying to add some global variables to my stylesheets and I want to do it with javascript (I thought there may be a way to use global variables without importing a css file containing those variables in each css file I'm creating):
let cssContent = `:root{ --mainColor:"#333";}
First I tried to create a new stylesheet file and put cssContent in there:
var blob = new Blob([cssContent]);
var url = URL.createObjectURL(blob);
var cssElement = document.createElement('link');
cssElement.setAttribute('rel', 'stylesheet');
cssElement.setAttribute('type', 'text/css');
cssElement.setAttribute('href', url);
Then to add this stylesheet to head, first I removed all exsisting stylesheets, add cssElement and then all the other removed stylesheets so the cssElement be the first stylesheet in head element.
var cssElements = Array.from(document.head.getElementsByTagName('link'))
.filter(link => link.getAttribute('rel') == 'stylesheet');
if (cssElements && cssElements.length > 0) {
cssElements.forEach(cssEl => {
document.head.removeChild(cssEl)
});
The behavior of removing and adding stylesheets works fine
But the :root element goes after the defined rule for body and does not apply:
Then I tried to add this variables to each css file instead of creating new one:
for (let index = 0; index < document.styleSheets.length; index++) {
document.styleSheets[index].insertRule(cssContent, 0);
}
Again same thing happened to previous approach, happens here too:
At last I tried to add this variables to each rule, but I couldn't find an approach
Is there any way to do this?

you can use these three:
document.documentElement.style.setProperty("--mainColor", "#333");
Element.setAttribute()
OR
document.documentElement.style.cssText = "--mainColor: #333";
Document.documentElement
OR
document.documentElement.setAttribute("style", "--mainColor: #333");
CSSStyleDeclaration.setProperty()

Related

Inject CSS variable names that come form the server (Angular 12)

I'm trying to find the best solution to inject css variables into angular application (currently using Angular 12).
This is the code I'm using:
private injectStyles(data: any) {
const styles = this.document.createElement('STYLE') as HTMLStyleElement;
styles.id = 'dynamic-css';
this.test = `:root {
--main-bg-color: black;
}`;
styles.innerHTML = this.test;
this.renderer.appendChild(this.document.head, styles);
}
This code is being executed on app.component.ts file and this works quite well, i can use this css variable throughout the whole application.
What I'm trying to achieve now is to extend the this.test Object with data that comes from the server and apply these custom css values that were set on my Visual settings module before.
Css variables must not have "" quotes.
"--button-color": "#a86c6c" (this is what I get from the server) and I would like to inject this property without quotes on the this.test Object and I can't do that. Any ideas?
Any help is highly appreciated,
Thanks.
Object.keys is your friend
let dataString = '';
Object.keys(data).forEach(key => {
dataString += `${key}: ${data[key]};`
});
this.test = `:root {
--main-bg-color: black;
${dataString}
}`;
If needed, you may need to add quote to the value like this
`dataString += `${key}: "${data[key]};"`

Unload/remove dynamically loaded css files

After loading a css file like this:
const themes = ['dark-theme.css', 'light-theme.css'];
async function loadcss(file) {
return await import(file);
}
loadcss(themes[0]).then(console.log)
The console output is an empty object for me and a new annonymous < style> tag sits in the < head> of my index.html. So far so good, but what if I (in this example) want to change the theme to light-theme.css. That would merge both themes as dark-theme.css is already loaded.
Is there a way to remove the < style> tag from the DOM?
To furthermore specify my question, the provided example shows an abstracted behaviour and I am only interested in removing the dynamically loaded css from the DOM.
I don't know vue.js but here is simple example in React hope it helps somehow :) perhaps some ideas at least :)
class TodoApp extends React.Component {
static themes = {
dark: 'dark-theme.css',
light: 'light-theme.css',
};
render() {
return ReactDOM.createPortal(
(<link rel="stylesheet" href={TodoApp.themes.dark} type="text/css"></link>),
document.head
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
http://jsfiddle.net/3y4hw2ox/
Thanks to OZZIE, I questioned my methodology and found, that importing the css files, like my question shows (through ES6 import, or require.context(...)), is not usefull, as we can't identify it, we dont get access to the <style> element, leaving us with no entry to the DOM and no way to manipulate it.
Instead we will link the css files manually in the <head>, as we know their name and path.
const themes = ['dark-theme.css', 'light-theme.css'];
const head = document.body.parentElement.firstElementChild;
const link = document.createElement('link');
link.setAttribute('href', process.env.BASE_URL + themes[0]);
link.setAttribute('id', themes[0]); // set id so we can remove it later
head.appendChild(link);

Export CSS of DOM elements

I often find nice stylings on the web. To copy the CSS of a DOM element, I inspect that element with Google Chrome Developer Tools, look at the various CSS properties, and copy those manually to my own stylesheets.
Is it possible to easily export all CSS properties of a given DOM element?
Here is the code for an exportStyles() method that should return a CSS string including all inline and external styles for a given element, except default values (which was the main difficulty).
For example: console.log(someElement.exportStyles());
Since you are using Chrome, I did not bother making it compatible with IE.
Actually it just needs that the browsers supports the getComputedStyle(element) method.
Element.prototype.exportStyles = (function () {
// Mapping between tag names and css default values lookup tables. This allows to exclude default values in the result.
var defaultStylesByTagName = {};
// Styles inherited from style sheets will not be rendered for elements with these tag names
var noStyleTags = {"BASE":true,"HEAD":true,"HTML":true,"META":true,"NOFRAME":true,"NOSCRIPT":true,"PARAM":true,"SCRIPT":true,"STYLE":true,"TITLE":true};
// This list determines which css default values lookup tables are precomputed at load time
// Lookup tables for other tag names will be automatically built at runtime if needed
var tagNames = ["A","ABBR","ADDRESS","AREA","ARTICLE","ASIDE","AUDIO","B","BASE","BDI","BDO","BLOCKQUOTE","BODY","BR","BUTTON","CANVAS","CAPTION","CENTER","CITE","CODE","COL","COLGROUP","COMMAND","DATALIST","DD","DEL","DETAILS","DFN","DIV","DL","DT","EM","EMBED","FIELDSET","FIGCAPTION","FIGURE","FONT","FOOTER","FORM","H1","H2","H3","H4","H5","H6","HEAD","HEADER","HGROUP","HR","HTML","I","IFRAME","IMG","INPUT","INS","KBD","KEYGEN","LABEL","LEGEND","LI","LINK","MAP","MARK","MATH","MENU","META","METER","NAV","NOBR","NOSCRIPT","OBJECT","OL","OPTION","OPTGROUP","OUTPUT","P","PARAM","PRE","PROGRESS","Q","RP","RT","RUBY","S","SAMP","SCRIPT","SECTION","SELECT","SMALL","SOURCE","SPAN","STRONG","STYLE","SUB","SUMMARY","SUP","SVG","TABLE","TBODY","TD","TEXTAREA","TFOOT","TH","THEAD","TIME","TITLE","TR","TRACK","U","UL","VAR","VIDEO","WBR"];
// Precompute the lookup tables.
for (var i = 0; i < tagNames.length; i++) {
if(!noStyleTags[tagNames[i]]) {
defaultStylesByTagName[tagNames[i]] = computeDefaultStyleByTagName(tagNames[i]);
}
}
function computeDefaultStyleByTagName(tagName) {
var defaultStyle = {};
var element = document.body.appendChild(document.createElement(tagName));
var computedStyle = getComputedStyle(element);
for (var i = 0; i < computedStyle.length; i++) {
defaultStyle[computedStyle[i]] = computedStyle[computedStyle[i]];
}
document.body.removeChild(element);
return defaultStyle;
}
function getDefaultStyleByTagName(tagName) {
tagName = tagName.toUpperCase();
if (!defaultStylesByTagName[tagName]) {
defaultStylesByTagName[tagName] = computeDefaultStyleByTagName(tagName);
}
return defaultStylesByTagName[tagName];
}
return function exportStyles() {
if (this.nodeType !== Node.ELEMENT_NODE) {
throw new TypeError("The exportStyles method only works on elements, not on " + this.nodeType + " nodes.");
}
if (noStyleTags[this.tagName]) {
throw new TypeError("The exportStyles method does not work on " + this.tagName + " elements.");
}
var styles = {};
var computedStyle = getComputedStyle(this);
var defaultStyle = getDefaultStyleByTagName(this.tagName);
for (var i = 0; i < computedStyle.length; i++) {
var cssPropName = computedStyle[i];
if (computedStyle[cssPropName] !== defaultStyle[cssPropName]) {
styles[cssPropName] = computedStyle[cssPropName];
}
}
var a = ["{"];
for(var i in styles) {
a[a.length] = i + ": " + styles[i] + ";";
}
a[a.length] = "}"
return a.join("\r\n");
}
})();
This code is base on my answer for a slightly related question: Extract the current DOM and print it as a string, with styles intact
I'm quoting Doozer Blake's excellent answer, provided above as a comment. If you like this answer, please upvote his original comment above:
Not a direct answer, but with Chrome Developer Tools, you can click inside Styles or Computed Styles, hit Ctrl+A and then Ctrl+C to copy all the styles in those given areas. It's not perfect in the Style tab because it picks up some extra stuff. Better than selecting them one by one I guess. – Doozer Blake 3 hours ago
You can do the same using Firebug for Firefox, by using Firebug's "Computed" side panel.
There are a few ways to almost do this.
Have a look at FireDiff
Also have a look at cssUpdater This is for local CSS only]
And see this Q for more similar tools: Why can't I save CSS changes in Firebug?
Also this paid product claims to be able to do this: http://www.skybound.ca/

css not class select?

Theres a mistake in my rather large demo where i assume all the divs under the class special will be used to align something. Now i realize i need to add an extra div outside of the part i want to align but inside of .special.
How do i write .special div[NOT someclass] ? or is there no way to do this and i need to rewrite a lot of html?
CSS3 includes the not() selector. The only problem is (you guessed it) no IE compatibility. If you're willing to require Javascript from IE <9 users, you can get IE compatibility with IE9.js.
+1 to both answers above.
I'll add i was able to get away with some things but writing this in the css block to undo the effect
some-type: inherit;
I would go with jQuery or some other Javascript Framework, the selectors just rock and NOT class XY is rather easy to achieve.
As Pekka pointed out I am not sure what brothers you want to target. getElementsByClassName() is implemented by almost all browsers (you know which one doesn't work, don't you?).
I found a rather nifty solution on devshed to also make it work in IE:
onload=function(){
if (document.getElementsByClassName == undefined) {
document.getElementsByClassName = function(className)
{
var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)");
var allElements = document.getElementsByTagName("*");
var results = [];
var element;
for (var i = 0; (element = allElements[i]) != null; i++) {
var elementClass = element.className;
if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass))
results.push(element);
}
return results;
}
}
}
All you need to do now is to iterate through all your div classes and negate the one you DON'T want.

How to call images from CSS when page are loaded using https

This one is driving me nuts. It's (yet) another IE6/7 idiosyncrasy, but one of my web pages needs to be loaded using https. In IE6/7 I get the dreaded "contains secure and nonsecure items" message which is causing users to panic. I've gone through the code top to bottom and isolated the problem (as IE sees it) to background images in my CSS. However, these use absolute paths...
background: url(/images/imagename.jpg);
Looks like this is tripping up IE and causing the nonsecure message on https. Anybody got any ideas how to get around this? Any help much appreciated.
That shouldn't be causing you any troubles, as long as the CSS file itself is also coming from HTTPS. Absolute paths without an explicit protocol (i.e. /path/to/file instead of http://example.com/path/to/file) inherit the protocol of the file calling them, be it HTML or CSS.
Can we see your page? It's possible there's something else on the page you're overlooking.
You are correct, relative url paths in background style will cause this message to appear in IE6/7.
The only method I have used successfully, is to either build the absolute path from available browser data, or to hard code the absolute path. Here is an example of how you can build the absolute path with JavaScript:
Using a top level style definition like this:
<style type="text/css">
.fixBgImage {
background: url(/images/imagename.jpg);
}
</style>
You can use a JavaScript function that looks up that rule, and changes the backgroundImage style for that rule. (Keep in mind that this example assumes you've defined the rule on sheet[0])
// this function needs to be run after the page has loaded
// (body.onload, window.onload or something similar)
function fixBackgroundImages() {
// using sheet 0 defined first on this page
var rule = getRule('.fixBgImage', document.styleSheets[0]);
if (rule != null) {
var bgUrl = rule.style.backgroundImage.replace(/^url|[\(\)]/g, '');
bgUrl = fixHttpsBgUrl(bgUrl);
rule.style.backgroundImage = 'url("' + bgUrl + '")';
}
}
function getRule(name, sheet){
var rules = (sheet.rules) ? sheet.rules : sheet.cssRules;
for (var i = 0; i < rules.length; i++) {
if (rules[i] && rules[i].selectorText == name) {
return rules[i];
}
}
return null;
}
// This function returns an absolute path if https is used
function fixHttpsBgUrl(imgUrl){
if (document.location.protocol.indexOf('https') >= 0){
var basepath = document.URL.substring(0, document.URL.lastIndexOf('/') + 1);
var pcol = document.location.protocol + '//';
var host = document.location.hostname;
var port = (document.location.port) ? ':' + document.location.port : '';
if (imgUrl.indexOf('/') == 0){ // server root path
imgUrl = pcol + host + port + imgUrl;
}
else{ // app root
imgUrl = basepath + imgUrl;
}
}
}
Try with:
background: url(//images/imagename.jpg);
According to this answer that should work. Try using it for the stylsheet as well, eg:
<link rel="stylesheet" type="text/css" src="//style/style.css" />
IE should have absolutely no problem with relative-pathed images so long as they're relative to a secure root. The problem you're hitting quite likely is caused elsewhere.
http://blogs.msdn.com/ieinternals/archive/2009/06/22/HTTPS-Mixed-Content-in-IE8.aspx

Resources