How to dynamically create CSS class in JavaScript and apply?
Here is an option:
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.cssClass { color: #F00; }';
document.getElementsByTagName('head')[0].appendChild(style);
document.getElementById('someElementId').className = 'cssClass';
Dynamically generated css
Another benefit would be that each additional asset that is loaded in the page slows down the overall page load because a browser is typically only loading 4 assets simultaneously at a given time.
However, doing all of that combining on page load is likely to cause the page to slow down just as much from the processing alone... depending on how many things it needs to iterate through to make up the final css block.
In terms of performance, it seems very unlikely to me that javascript is going to elegantly / efficiently replace the styles that would've been included in a combined css file.
The other thing is that in production environments, you also can do another option which is to use a library to pre-generate a set of minified, optimized, (and obfuscated in the case of JS) code and then have 1 js file and 1 css file loaded at the time of page load, which in-turn you'd cache for speed/loading benefits. Boilerplate lib has a script for this i think.
End of the day: energy is neither created nor destroyed -- your optimizations in one place and hurt you elsewhere if you're not careful.
Generating dynamic CSS
However, serving CSS as a view seems like it would cause a significant amount of overhead in a file that is constantly requested, so this is probably not a good solution.
And what if you would generate that CSS once?
- Default CSS is:
/common/css.css
- Member customize CSS, now
<link />
elements points to/user-specific/123.css?ts=123123123
.123
is of course an identifier of the member, andts
parameter contains a timestamp - a date of last CSS modification - Make sure that your CSS generator sets proper HTTP headers responsible for client-side caching
- User browser request a CSS file - server replies with simple
304 Not Modified
header - there is no need for any script execution or contents download - When member modifies his CSS then you just update
ts
- once again just a single request is needed
Generate css dynamically via templates and placeholders
Wound up playing with this and CSS variables. I'm adding a second answer because it's very different method from my first answer and it better aligns with your original question (updating CSS variables with JS).
BUT... don't do this. :) Browser support in IE < Edge doesn't exist and it is almost certainly slower than updating an on-page <style>
element though I haven't tested it. This jsperf tests various style update methods. It doesn't include innerHTML
on a single style
element (likely the fastest) but you can see that the following CSS DOM methods are slower than the rest.
// get the stylesheet
// array position depends on how many style sheets you're loading.
// adjust as needed.
var sheet = document.styleSheets[0];
// simplest method: insertRule()
// setTimeout only for demo so you can see the change
window.setTimeout(function(){
// @media all {} is a trick to insert more than one
// selector and/or properties at once. Otherwise it's:
// sheet.insertRule(":root", "--header-color: green"); ...repeat...
sheet.insertRule("@media all { :root { --header-color: green; --main-color: orange; } }", 1);
}, 1200);
// SAFER method via addCSSRule.
// button and getAjaxStyles are just placeholders, obviously
var btn = document.querySelector('button');
btn.addEventListener("click", getAjaxStyles);
function getAjaxStyles() {
// success callback... break apart the json and update the CSS variables
addCSSRule(sheet, ":root", "--header-color: orange");
addCSSRule(sheet, ":root", "--main-color: blue");
addCSSRule(sheet, ":root", "--alt-color: red");
addCSSRule(sheet, ":root", "--borderColorA: lavender");
// or go with a single big string. definitely faster:
// addCSSRule(sheet, ":root", "--alt-color: red; --borderColorA: #0ff; ")
}
// Credit for addCSSRule() goes to Diego Flórez in a comment on
// https://davidwalsh.name/add-rules-stylesheets
var addCSSRule = function(sheet, selector, rules) {
//Backward searching of the selector matching cssRules
var index = sheet.cssRules.length - 1;
for (var i = index; i > 0; i--) {
var current_style = sheet.cssRules[i];
if (current_style.selectorText === selector) {
//Append the new rules to the current content of the cssRule;
rules = current_style.style.cssText + rules;
sheet.deleteRule(i);
index = i;
}
}
if (sheet.insertRule) {
sheet.insertRule(selector + "{" + rules + "}", index);
} else {
sheet.addRule(selector, rules, index);
}
return sheet.cssRules[index].cssText;
}
/* Set initial CSS variables */
:root {
--header-color: #333;
--main-color: #888;
--alt-color: #bbb;
--borderColorA: #ccc;
}
h1 {
color: var(--header-color);
}
p {
border-bottom: 1px solid var(--borderColorA);
color: var(--main-color);
}
p+p {
color: var(--alt-color);
}
<h1>header</h1>
<p>paragraph 1</p>
<p>paragraph 2</p>
<button>Update CSS Variables</button>
Generate dynamic css based on variables angular
Direct approach available in angular is using ngstyle as follows
<div [ngStyle]="{'color': style.colorVal ? style.colorVal : '#000', 'font-size' : style.fontSize ? style.fontSize : '16px' }"></div>
After going through different methods and approached to add dynamic css to all pages on angular app I ended up with following solutions.
Requirement : generate dynamic css based on values returned from and API to change design and styling.
Solution :
- create a new component and create a service to load dynamic css variables from API.
- Add style tag in template file and use variable values for properties.
- Load this template on all pages or on main template.
- On app build style will be moved to head tag.
Code sample
import { CssService} from './Css.service';
@Component({
selector: 'DynamicCss',
templateUrl: './DynamicCss.component.html',
styleUrls: ['./DynamicCss.component.scss']
})
export class ServiceProviderComponent implements OnInit {
cssVariables: any;
constructor(private cssService:CssService){
/* call the service/api to get the css variable values in cssVariables */
}
}
Now apply css using jquery or javascript to append css with help of function like following
appendCss(customData)
{
let text = '.custom-form-1 {
background-image: url("`+customData.background_image+`");
}';
$(document).ready(function(){
$("style").append(text);
});
}
and call this function after loading custom data from service or other variable like I did it ngOnInit
ngOnInit(){
this.appendCss(this.customizeFormData);
}
Its using jquery but can be done with javascript/typescript as well if you dont want to use jquery in your angular app
Other useful resource https://github.com/angular/angular/issues/9343#issuecomment-312035896
How to dynamically generate CSS class and/or set its property
What you are trying to do is the subject of this open issue about stylesheet binding in Angular. Until that feature is available, you can get what you want with a custom directive. Here is a directive that retrieves the checkbox element generated by ng-zorro-antd
and applies two color attributes to it. The two colors are @Input
properties and the directive implements OnChanges
which allows to react to property binding changes.
@Directive({
selector: "[nz-checkbox][nz-chk-style]"
})
export class CheckBoxStyleDirective implements OnInit, OnChanges {
@Input("nz-chk-bkgnd") chkBkgndColor: string;
@Input("nz-chk-border") chkBorderColor: string;
private checkbox: HTMLElement;
constructor(private renderer: Renderer2, private el: ElementRef) { }
ngOnInit() {
this.checkbox = this.el.nativeElement.querySelector(".ant-checkbox-inner");
this.updateBackgroundColor();
this.updateBorderColor();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.chkBkgndColor) {
this.updateBackgroundColor();
}
if (changes.chkBorderColor) {
this.updateBorderColor();
}
}
updateBackgroundColor() {
if (this.checkbox) {
this.renderer.setStyle(this.checkbox, "background-color", this.chkBkgndColor);
}
}
updateBorderColor() {
if (this.checkbox) {
this.renderer.setStyle(this.checkbox, "border-color", this.chkBorderColor);
}
}
}
Once the directive attribute selector nz-chk-style
is applied to the 3rd party element, you can set the checkbox background and border colors with property binding as follows:
<span nz-checkbox nz-chk-style [nz-chk-bkgnd]="bkgndColor" [nz-chk-border]="borderColor" >
See this interactive stackblitz for a demo.
Generate CSS classes dynamically based on variables - SCSS
use @each
loop. Instead of creating the vars
in :root
add those in a single var
(see below example)
$colors : (
"primary-50": "1,1,1",
"primary-100": "2,2,2",
"primary-200": "3,3,3",
);
@each $color, $value in $colors {
.bg-#{$color} {
background-color: rgb($value);
}
}
the above code compiled into
.bg-primary-50 {
background-color: #010101;
}
.bg-primary-100 {
background-color: #020202;
}
.bg-primary-200 {
background-color: #030303;
}
And for CSS --variables
:root {
@each $color, $value in $colors {
--color-#{$color}: rgb($value);
}
}
and you have CSS Variables
:root {
--color-primary-50: #010101;
--color-primary-100: #020202;
--color-primary-200: #030303;
}
Like you mentioned in your comment "will this solution work for the light and dark modes?" for that you can do something like this
html[data-color-mode="dark"] {
$dark-mode-colors: (
"primary-color-50": "0, 0, 0",
"primary-color-100": "1, 1, 1",
"primary-color-200": "2, 2, 2",
)
@each $color, $value in $colors {
.bg-#{$color} {
background-color: $value;
}
}
}
// change your color scheme as you prefer method will remain the same
html[data-color-mode="light"] {
$light-mode-colors: (
"primary-color-50": "0, 0, 0",
"primary-color-100": "1, 1, 1",
"primary-color-200": "2, 2, 2",
)
@each $color, $value in $colors {
.bg-#{$color} {
background-color: $value;
}
}
}
Related Topics
Why Do Flexbox Item Images Resize Differently According to Their Initial Size
How to Turn Off SASS Rgb -> Color Name
Ckeditor - Prevent Adding Image Dimensions as a CSS Style
Tweaking Bootstrap 2.0 for Semantic Markup
What Is the Current State of Sub-Pixel Accuracy in the Major Browsers
Svg "Fill: Url(#....)" Behaving Strangely in Firefox
What Are Cross-Browser, Cross Platfom Web Safe Fonts
Twitter Bootstrap 3 How to Activate Navbar-Collapse on Small Devices
How to Make Nginx Serve Static Content Like .Js, .Css, .Html
CSS - Circle with Margin on Border
Css: Rotate Image and Align Top Left
Use CSS to Hide Contents on Print