How do I apply a stylesheet to an element using Javascript - javascript

I need to be able to change out the css applied to the <body> on the fly. How, using javascript, could this be accomplished?
My css is stored as a string in a Javascript variable. I am not working with css files. The css consists of around 50 classes, so it doesn't make sense to apply them one-by-one. I know how this could be accomplished by changing the lowest class, but I'm just trying to see if it's possible to do using Javascript commands and variables.
Pseudo Code
var x = "#nav-bar-wrapper {
background-color: #4a3e7d;
padding: 20px 0;
}
#header-nav {
display: flex;
justify-content: center;
}...";
function changeCss() {
var el = $('myelement');
SomeJavaScriptFunction(el, x);
}

As #evolutionbox pointed out, it looks like you want to add new styles to the page. If so, just add them as text content in a style element:
const css = '#nav-bar-wrapper { ... }'
function changeCss() {
const el = document.createElement('style')
el.textContent = css
document.head.appendChild(el)
}

One easy way of doing it would be:
document.querySelector("yourElement").style.cssText += "Your super long string will go here and itll work"
Although, I dont think theres a way of giving different elements a uniqe style all in one line other than appending a style tag to the html. To use the method above, you'd have to separate #nav-bar-wrapper and #header-nav
Just keep in mind to use `` rather than "" to make the string go onto multiple lines

Here's a more JavaScript-y method than using hacky style tags.
const button = document.getElementById('button');
button.onclick = () => {
[
[ 'background', 'black' ],
[ 'color', 'white' ]
].map(([ prop, val ]) => {
button.style[prop] = val;
});
};
<button id="button">Hello there</button>
You could also try out https://cssinjs.org, which also works great in React.
Perhaps using jQuery's .css() function is worthy too: https://api.jquery.com/css/. It can take an object of CSS properties and apply them to an element.
$('#button').on('click', () => {
$('#button').css({ background: 'black', color: 'white' });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<button id="button">Hi there</button>
You could also create a new style tag and append it to the body as many people have said. You can even append an external link rel="stylesheet" tag to the header to dynamically add a stylesheet from another URL! An alternative to this would be using the fetch API/jQuery AJAX/xmlhttprequest plus the code below:
$('#button').on('click', () => {
$('body').append('<style>#button { background: black; color: white; }</style>');
});
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<button id="button">Hi there</button>
</body>
Another approach is using classes that you can dynamically add in JavaScript.
$('button').on('click', () => $('#button').addClass('clicked'));
.clicked {
background: black;
color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<button id="button">Hi there</button>

You need to get a reference of your html element and after manipulate the DOM, here is an example:
document.getElementById('yourElementIdHere').style.color = 'red';
If you want to get your body reference to manipulate the DOM, you can do it. After this, you can change the style of your body using the style property, and use your string with your css.
document.querySelector("body").style = yourCssStringVar;
This link can help you, too:
JavaScript HTML DOM - Changing CSS

Related

How do i change the color of an appended string?

I've a div
<div class="display-container"></div>
Inside this div i want to append some text using a JavaScript event listener
const calculatorDisplay = document.querySelector(".display-container")
function appendNumber(number) {
calculatorDisplay.append(number)
}
// number event listener
one.addEventListener("click", () => {
calculatorDisplay.append(1)
})
it work perfecly, but the problem here is that the background color of the display-container div is black, and the default color for string is black, so, how do i change the color of an appended string?
i've already tried using the style tag, but that does not work, i've tried using fontcolor() too, but that too doesn't worked.
I've noticed that the appended string have an id of #text, but i cannout use it if i try.
Define css class
<style>
.colored-text {
color: red;
}
</style>
And then create span element with colored-text class and append it
// number event listener
one.addEventListener("click", () => {
const newSpan = document.createElement('span');
newSpan.classList.add('colored-text');
newSpan.textContent = 1;
calculatorDisplay.append(newSpan);
})
BTW. why are you defining appendNumber function and not using it?
There are several ways to achieve this.
javascript
const calculatorDisplay = document.querySelector(".display-container")
// changing the color to red
calculatroDisplay.style.color = 'red';
// it accepts also Hex colors
calculatorDisplay.style.color = '#FF5733'
// OR rgb
calculatorDisplay.style.color = 'rgb(255,0,0)
CSS
It is also possible to append a classname to your div. Like this you could
make the code probably more reusable and may apply more styles than just colors in a simple manner. (There are multiple ways to include CSS in your html, google it^^ )
// within in the <head> tag in the html add a <style> tag.
<html>
<head>
<style>
.red-color {
color: red
}
</style>
</head>
<body>
<!-- ..... --->
</body>
</html>
In the code you can now add a classname using element.classList.add() OR element.classList.remove() to remove classes!
function setRedColor(el) {
el.classList.add('red-color')
}
function removeRedColor(el) {
el.classList.remove('red-color')
}
const calculatorDisplay = document.querySelector(".display-container")
setRedColor(calculatorDisplay)
// ...
removeRedColor(calculatorDisplay)
Note that the element.classList API generally does not allow classnames with a whitespace in it. So if you have mutliple classes you have to apply them one by one or you'll run into an error.
Feel free to leave a comment

How can i minimize the code in a class toggler that targets nav?

I love clean code but I'm zero in javascript. I'd love to do two things to the super easy code below:
function nav_open() {
var myNav = document.getElementById('nav_anim');
if (myNav.className == 'nav_closed') {
myNav.className = 'nav_open';
} else {
myNav.className = 'nav_closed';
}
}
Use getElementsByTagName to target the nav instead of giving it a useless id (in order to use only <nav> instead of <nav id="nav_anim"> in the body. I tried some combos but none of them works.
Get rid of that ugly myNav name, is it mandatory? I know I can change it, but can I remove it? Is it possible to use something like
nav.className=='nav_closed' or even better className=='nav_closed' instead of myNav.className=='nav_closed'
I would suggest keeping the id on your nav, targetting your DOM elements using ids or classes is something that is commonly done and can speed up lookup. Using getElementsByTagName() adds unnecessary complexity and would need to traverse your entire DOM to find your element, so it isn't very efficient, espeicially if you just have one nav element. If you really want to, you could use querySelector to select the first nav item:
const myNav = document.querySelector("nav");
At the end of the day though, if you want to interact with elements in your JavaScript code, you'll need to explicitly grab them (not counting named access on the global window object as it is recommended not to use this).
To further improve your toggle code, you could perform two toggles using DOMTokenList.toggle(), one to hide your first class and one to add your other. Every time you run both toggles, they will add/remove both classes depending on whether they exist or not:
const myNav = document.getElementById("nav_anim");
myNav.classList.toggle('nav_closed');
myNav.classList.toggle('nav_open');
See example below:
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
btn.classList.toggle("on");
btn.classList.toggle("off");
});
.on {
background-color: lightgreen;
}
.off {
background-color: red;
}
<button id="btn" class="on">Click me</button>
Depending on your code, you may even be able to remove the nav_closed class by targeting your nav element that does not have the nav_open class:
#nav_anim:not(.nav_open) {
/* nav_closed styles */
}
With this setup, you can use just one toggle:
const myNav = document.getElementById("nav_anim");
myNav.classList.toggle('nav_open');
See example below:
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
btn.classList.toggle("on");
});
.on {
background-color: lightgreen;
}
#btn:not(.on) {
background-color: red;
}
<button id="btn" class="on">Click me</button>
You should be able to do this with a conditional (ternary) operator, like so:
var nav = document.getElementsByTagName("nav")[0];
(nav.classList.contains('nav_open')) ? nav.classList.remove('nav_open') : nav.classList.add('nav_open'));
This is like a 1 line if statement with 3 parameters:
condition ? exprIfTrue : exprIfFalse

Create CSS using js?

So In javascript you can do things like
document.querySelector('html').style.filter = 'invert(100%)'
Which Inverts colors on the entire webpage
but is there anyway to use
document.querySelector('html').style.pointer = 'something'
or a way to
add a css rule or something to the document?
example
you have an element like this
hello
then js gives an class
hello
then js adds a css rule?
.why {}
You could write to the document using document.write
document.write(`
<style>
#text {
color: red;
}
</style>`)
<h1 id='text'>Hello</h1>
Or, you can also create an element and append it to the document
let style = document.createElement('style')
style.innerHTML = `
.text {
color: red;
}
`
document.body.appendChild(style)
<h1 class='text'>Hello</h1>
You can add a css class to specific element with (and then create the styles from the css file):
[NameOfTheElement].classList.add("mystyle")
This will work for the document:
document.querySelector('html').classList.add('new-class');
This will work for the body:
document.querySelector('body').classList.add('new-body-class')
There are a few ways.
If you already have styles defined for a certain class within your stylesheet, you can do what Erasmo said above:
element.classList.add('className');
You can also use:
element.style.color = 'white';
Or add a style attribute to the element:
element.setAttribute('style', 'color: white; background-color: green;');

Generate css dynamically via templates and placeholders

I want to generate css dynamically at run time.
Initially I had used sass and defined some variables and was using those variables. But css has to be generated first from the scss. Sass had given me flexibility to use variables and functions but still I was not able to changes them at run time via javascript.
One way was to change the inline styles via javascript but that approach was not completly flexible.
document.getElementById("myDiv").style.color = "red";
I don't want to do above, neither I want to attach any <style> attribute via javascript.
I want to use javascript but not for chaniging each and every style properties. I want to achieve scss like effect using css and javascript but at run time i.e dynamically.
E.g. suppose I got the color information from the ajax call now I want to change the whole theme of website based on that color received immediately without restarting or re-deploying my application.
e.g
as done in scss
.myClass {
background:$color;
// Update color value dynamically at run-time
}
Is it even possible or I am thinking in wrong direction!
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>
To expand on the information that is provided in the linked "possible duplicate" question, you could easily set up a "default" set of styles in your page CSS file and then create a inline <style> ekement containing any overrides based on the response from your AJAX call. As long as the element/class/id definitions are the same in the two locations (i.e., CSS file and inline style section), specificity will cause the inline definitions to override the CSS ones.
So, using your example, your static CSS file would contain:
.myClass {
background: #FFFFFF;
}
. . . so that there is a default value if the AJAX call were to fail, and then your dynamically created <style> section would contain:
.myClass {
background: THE_AJAX_RESPONSE_VALUE;
}
. . . which would override the default value.
UPDATE #1:
Based on your sample JSON, this would be REALLY easy . . . you would loop through each top-level property of the JSON and create this:
KEY_NAME {
. . .
}
Then, within that block, loop through each property within that property and add the keys and values to create the style definitions:
KEY_NAME {
key1: value1,
key2: value2,
. . .
keyN: valueN
}
UPDATE #2:
You can also use StyleSheet and CSSStyleSheet interfaces to access the rules that are in the existing stylesheets, but, given that it uses an array-like structure, that means looping through all of the CSS definitions to find the one that you want and alter it. An example of how to do that can be found in this answer to another SO question: Is it possible to alter a CSS stylesheet using JavaScript? (NOT the style of an object, but the stylesheet itself)
Between the two approaches, though, creating an overriding <style> section seems like the easier approach.
Since the JSON has both the element names and the related styles, refreshing an on page stylesheet (vs inline element styles) would probably be the fastest since it uses innerHTML and only requires a single DOM lookup.
You'll need to loop through your JSON to create CSS compatible strings and then just dump it into the onpage style element. You can append CSS by concatenating the existing innerHTML with the new CSS string. I added an ID to the stylesheet for simplicity but you could also generate the style element when needed.
var StringifiedAjaxStyleObject = "h1 {background-color: #ecc; color: #633}";
var styleSheet = document.getElementById("style-update");
// some additional fake test style returns...
var testStyle1 = "h1 {background-color: #ccc; color: #333}";
var testStyle2 = "h1 {background-color: #667; color: #bbc}";
var testStyle3 = "h1 {background-color: #fee; color: #b00}";
// some fake ajax returns...
window.setTimeout(function() {
styleSheet.innerHTML = StringifiedAjaxStyleObject;
}, 1000);
window.setTimeout(function() {
styleSheet.innerHTML = testStyle1;
}, 2000);
window.setTimeout(function() {
styleSheet.innerHTML = testStyle2;
}, 3000);
window.setTimeout(function() {
styleSheet.innerHTML = testStyle3;
}, 4000);
/* base styles ... */
h1 {
padding: 5px;
background-color: #eef;
color: #007
}
<!-- empty stylesheet -->
<style id="style-update" type="text/css" rel="stylesheet"></style>
<h1>Hi, mom</h1>
<button>Update Styles<button>
EDIT:
Here's a slightly more real-world version based on the JSON object in your comment. Trigger it via the button.
var styleSheet = document.getElementById("style-update");
var btn = document.querySelector('button');
btn.addEventListener("click", updateStyles);
function updateStyles() {
var StringifiedAjaxStyleObject
, newCSS
, ajaxReturn
;
// ...your ajax method to get the new styles...
// on success...
ajaxReturn = {
".base": {
"background-color": "#b83605",
"border-color": "#543927",
"color": "gray",
"text-shadow": "0 -1px 0 rgba(0, 0, 0, 0.15)"
},
".overlay": {
"background": "rgba(76, 65, 80, 0.2)",
"color" : "#ddd"
}
};
// Convert the object to a string
newCSS = cssStringFromJson(ajaxReturn);
// Update the stylesheet
styleSheet.innerHTML = newCSS;
}
function cssStringFromJson(cssJSON) {
var styleStr = "",
i, j;
for (i in cssJSON) {
styleStr += i + " {\n"
for (j in cssJSON[i]) {
styleStr += "\t" + j + ": " + cssJSON[i][j] + ";\n"
}
styleStr += "}\n"
}
return styleStr;
}
/* base styles ... */
.base {
border: 1px solid #ccf;
background-color: #eef;
color: #000;
padding: 15px;
}
.overlay {
padding: 5px 15px;
background: rgba(96, 95, 180, 0.2);
}
body {
font-family: sans-serif;
}
button {
margin-top: 1em;
font-size: 1em;
}
<!-- empty stylesheet -->
<style id="style-update" type="text/css" rel="stylesheet"></style>
<div class="base">
<p>.base</p>
<div class="overlay">
<p>.overlay</p>
</div>
</div>
<button>Update Styles</button>
You can try angular templates.
It is going to break your previous sass, but it will work out later.

How can I programmatically alter CSS to limit its scope?

I've got a chunk of CSS as a string, and I want to insert it into the DOM such that it only applies to elements in a particular container. Some tools, like Polymer, for example, rewrite CSS selectors so they only apply within a limited scope. How can I do something similar so that, when I insert this CSS into the DOM, it won't change all elements on the page?
To make it more concrete, imagine the following HTML and CSS from an external source:
<style>p { font-size: 20px; }</style>
<p>Boo.</p>
I want to insert these elements into a #container element, but I don't want to change the font-size for all <p> elements. I'd like to rewrite all the selectors inside that <style> element so they only apply within #container (p -> #container p, etc.). How?
Use https://github.com/reworkcss/css to parse the CSS, then alter selectors, and finally stringify:
const CSS = require('css');
function scopeCSS(css, scope) {
const ast = CSS.parse(css);
for (let rule of ast.stylesheet.rules) {
if (rule.type == 'rule') {
rule.selectors = rule.selectors.map(selector => `${scope} ${selector}`);
}
}
return CSS.stringify(ast);
}
scopeCSS('div { color: black; }', '#foo');
// #foo div {
// color: black;
// }
http://requirebin.com/?gist=trevordixon/839d0674531dafa98fb95ae51474245e

Categories