Is it possible to alter a CSS stylesheet using JavaScript? (NOT the style of an object, but the stylesheet itself)
Is it possible to alter a CSS stylesheet using JavaScript?
I am NOT talking about:
document.getElementById('id').style._____='.....';
I AM talking about altering:
#id {
param: value;
}
besides doing something dirty (which we haven’t tried yet btw), like creating a new object in the head, innerHTML a style tag in there, etc. Although this, even if it did work, would pose a few issues as the style block is already defined elsewhere, and I’m not sure when/if the browser would eve开发者_StackOverflown parse a dynamically created style block?
Yes you can; every browser supports this, including IE9+).
The
insertRule()
method allows dynamic addition of rules to a stylesheet.With
deleteRule()
, you can remove existing rules from a stylesheet.Rules within a stylesheet can be accessed via the
cssRules
attributes of a stylesheet.
We can use a combination of .insertRule
and .cssRules
to be able to do this all the way back to IE9:
function changeStylesheetRule(stylesheet, selector, property, value) {
// Make the strings lowercase
selector = selector.toLowerCase();
property = property.toLowerCase();
value = value.toLowerCase();
// Change it if it exists
for(var i = 0; i < stylesheet.cssRules.length; i++) {
var rule = stylesheet.cssRules[i];
if(rule.selectorText === selector) {
rule.style[property] = value;
return;
}
}
// Add it if it does not
stylesheet.insertRule(selector + " { " + property + ": " + value + "; }", 0);
}
// Used like so:
changeStylesheetRule(s, "body", "color", "rebeccapurple");
Demo
2020
Some advantages of this method:
- Does not require (but allows) stylesheet to be specified.
- Allows multiple styles to be added / modified at once
- Accepts
!important
attribute - Ignores extra whitespace when matching CSS selector
- Changes last matching existing rule, or appends to end of last matching stylesheet. (Other answers add/change the first rule which may be then overruled.)
Usage:
adjustCSSRules('#myDiv', 'width: 300px !important');
Method:
function adjustCSSRules(selector, props, sheets){
// get stylesheet(s)
if (!sheets) sheets = [...document.styleSheets];
else if (sheets.sup){ // sheets is a string
let absoluteURL = new URL(sheets, document.baseURI).href;
sheets = [...document.styleSheets].filter(i => i.href == absoluteURL);
}
else sheets = [sheets]; // sheets is a stylesheet
// CSS (& HTML) reduce spaces in selector to one.
selector = selector.replace(/\s+/g, ' ');
const findRule = s => [...s.cssRules].reverse().find(i => i.selectorText == selector)
let rule = sheets.map(findRule).filter(i=>i).pop()
const propsArr = props.sup
? props.split(/\s*;\s*/).map(i => i.split(/\s*:\s*/)) // from string
: Object.entries(props); // from Object
if (rule) for (let [prop, val] of propsArr){
// rule.style[prop] = val; is against the spec, and does not support !important.
rule.style.setProperty(prop, ...val.split(/ *!(?=important)/));
}
else {
sheet = sheets.pop();
if (!props.sup) props = propsArr.reduce((str, [k, v]) => `${str}; ${k}: ${v}`, '');
sheet.insertRule(`${selector} { ${props} }`, sheet.cssRules.length);
}
}
Demo
The method takes three arguments:
- selector [String] - CSS selector - eg: '#myDiv'
Whitespaces are auto-reduced (.myClass #myDiv
will match.myClass #myDiv
) - rules [CSS String, Object] - eg (either is acceptable):
{ border: "solid 3px green", color: "white" }
'border: solid 3px green; color: white'
- sheet (Optional) [String, StyleSheet]
- if empty, all stylesheets will be checked
- 'myStyles.css' A relative or absolute URL to sheet
document.styleSheets[1]
- A reference to a sheet
Other examples:
adjustCSSRules('#myDiv', {width: '30px'}); // all stylesheets
adjustCSSRules('#myDiv', 'width: 30px', 'style.css'); // style.css only
adjustCSSRules('#myDiv .myClass', 'width: 30px', document.styleSheets[0]); // only first stylesheet
When I want to programmatically add a bunch of styles to an object, I find it easier to programmatically add a class to the object (such class has styles asscociated with it in your CSS). You can control the precedence order in your CSS so the new styles from the new class can override things you had previously. This is generally much easier than modifying a stylesheet directly and works perfectly cross-browser.
change a property in a style rule
function change_css_style (titulo,selector,propiedad,valor) {
let i=0;
while (i<document.styleSheets.length) {
if (document.styleSheets[i].title==titulo) {
let y=0;
while (y<document.styleSheets[i].cssRules.length) {
if (document.styleSheets[i].cssRules[y].selectorText==selector) {
document.styleSheets[i].cssRules[y].style[propiedad] = valor;
y = document.styleSheets[i].cssRules.length;
}
y++;
}
i=document.styleSheets.length;
}
i++;
}
}
DEMO
<style title="chat_inicio">
.contenido .mensajes {
width: 100px;
height: 300px;
}
</style>
change the style book with the title chat_inicio with the selector .contenido .mensajes the property of the style width to 475px
<script>
cambiar_css_style ('chat_inicio','.contenido .mensajes','width','475px');
</script>
.style.cssText property works, try the code below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
*{
margin: 0%;
padding: 0%;
}
html {
--theme-orange: orangered;
--theme-blue: rgb(67, 67, 197);
--theme-green: darkgreen;
--theme-black: black;
--theme-color: var(--theme-orange);
}
body {
font-family: 'Roboto', sans-serif;
background-color: rgb(251, 251, 251);
}
.nav-bar ul {
display: flex;
width: 100%;
background-color: var(--theme-color);
flex-wrap: wrap;
flex-direction: row;
align-items: center;
width: 100%;
}
.nav-bar ul a {
text-decoration: none;
margin: 15px 10px;
}
.nav-bar .theme {
background-color: white;
display: flex;
height: fit-content;
margin-left: auto;
margin-right: 20px;
border-radius: 10px;
}
.nav-bar .theme .box {
width: 20px;
height: 20px;
border: 1px solid black;
cursor: pointer;
}
.nav-bar .theme .orange {
background-color: var(--theme-orange);
}
.nav-bar .theme .blue {
background-color: var(--theme-blue);
}
.nav-bar .theme .green {
background-color: var(--theme-green);
}
.nav-bar .theme .black {
background-color: var(--theme-black);
}
.nav-bar ul li {
color: white;
font-weight: 500;
list-style: none;
padding: 10px 30px;
background-color: var(--theme-color);
transition: 0.2s;
}
.nav-bar ul li:hover {
box-shadow: inset 10px 10px 10px -12px;
scale: 0.95;
}
</style>
<body>
<div class="nav-bar">
<ul>
<a href=""><li>Home</li></a>
<a href=""><li>Page 1</li></a>
<a href=""><li>Page 2</li></a>
<a href=""><li>About Us</li></a>
<a href=""><li>Contact Us</li></a>
<div class="theme">
<a><div class="box orange" id="orange"></div></a>
<a><div class="box blue" id="blue"></div></a>
<a><div class="box green" id="green"></div></a>
<a><div class="box black" id="black"></div></a>
</div>
</ul>
</div>
<script>
function colorChange(color) {
const htmlTag = document.getElementsByTagName("*")[0];
htmlTag.style.cssText = `--theme-orange: orangered;
--theme-blue: rgb(67, 67, 197);
--theme-green: darkgreen;
--theme-black: black;
--theme-color: var(--theme-${color});`;
}
function addEventListenerForBox() {
allBox = document.querySelectorAll('.box');
allBox.forEach(box => {
box.addEventListener('click', (event) => {
colorChange(event.target.id);
});
});
}
document.addEventListener('DOMContentLoaded', addEventListenerForBox);
</script>
</body>
</html>
Result:
One solution is:
Content CSS file:
#casesDndDropdown {
background: #FFFFFF;
border: 4px
}
You can override the #casesDndDropdown or any CSS class by defining it in the <style>
tag inside body,
jQuery
$('<style>#id{background: #428bca;border: 0px}</style>').appendTo('body');
2023/2
A few years ago I read in w3schools: HTML style Tag that the <style>
element supports the HTML Global Attributes and HTML Event Attributes.
The above means that, next to href
, rel
and target
, any stylesheet can be disabled by toggling its disabled
attribute. I had to dig deep to verify when and how this spec was implemented and found an old (November 2000) W3C document already mentioning support for the disabled
attribute of a stylesheet.
tl;dr
- putting
disabled
directly in the style definition like<style disabled>
does not work. - toggling the
disabled
attribute false/true with Javascript disables/enables the entire referenced<style>...</style>
block, wherever this block resides in your document. - Planning the position of style blocks in your document now becomes a matter of concern, as the regular browser logic is 'last in, first serve'.
All you need is a reference to a stylesheet element and a few Javascript oneliners:
function disabledToggle(e) { e.disabled = !e.disabled }
function disabledOff (e) { e.disabled = false }
function disabledOn (e) { e.disabled = true }
FYI
- Check out the
media
attribute specification in the above W3C document. - The
<link>
element also supports the mentioned HTML Global Attributes and HTML Event Attributes.
Essentially, it has always been possible to kill a stylesheet with a single hipshot.
A simple proof of concept:
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Alternate font, activated in default <style>, but can be toggled on/off -->
<link id="lnk-poppins" href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
<!-- Default styling, considered 'always active' -->
<style>
* { box-sizing: border-box }
body { font-family: Poppins, sans-serif }
.square {
margin: 5rem auto;
width : 50vmin; aspect-ratio: 1;
background-color: CornflowerBlue;
}
</style>
<!-- Media controlled style, only active on small devices -->
<style media="all and (max-width: 640px)">
body {
margin: 0; padding: 1rem;
width: 100%; min-height: 100vh;
background-color: hsl(90,100%,50%,.3);
}
</style>
<!-- Alternative styles: last in, first serve, so order matters -->
<style id="stl-red" >.square { background-color: Red }</style>
<style id="stl-green">.square { background-color: Green }</style>
<style id="stl-blue" >.square { background-color: Blue }</style>
<!-- Default style, but can be toggled: overrides all above when enabled -->
<style id="stl-default" >.square { background-color: Black }</style>
</head>
<body>
<fieldset>
<legend> Style Toggles </legend>
<p>Colors:</p>
<label for="default">
<input id="default" class="radio" type="radio" name="group" checked
oninput="disabledOff(defa);">
Default
</label>
<label for="red">
<input id="red" class="radio" type="radio" name="group"
oninput="disabledOff(red);disabledOn(defa);disabledOn(blue);disabledOn(green);">
Red
</label>
<label for="green">
<input id="green" class="radio" type="radio" name="group"
oninput="disabledOff(green);disabledOn(defa);disabledOn(blue);">
Green
</label>
<label for="blue">
<input id="blue" class="radio" type="radio" name="group"
oninput="disabledOff(blue);disabledOn(defa);">
Blue
</label>
<p>Font:</p>
<label for="poppins">
<input id="poppins" type="checkbox" oninput="disabledToggle(popp);" checked>
Poppins
</label>
<br><br>
<span>Old W3C Reference: <a target="_blank" href="https://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-disabled">Attributes: <b>disabled</b> of type boolean</a></span>
</fieldset>
<div class="square"></div>
<script>
const red = document.getElementById('stl-red');
const green = document.getElementById('stl-green');
const blue = document.getElementById('stl-blue');
const defa = document.getElementById('stl-default');
const popp = document.getElementById('lnk-poppins');
function disabledToggle(e) { e.disabled = !e.disabled }
function disabledOff (e) { e.disabled = false }
function disabledOn (e) { e.disabled = true }
</script>
</body>
</html>
精彩评论