I'm looking for a way to change the CSS rules for pseudo-class selectors (such as :link, :hover, etc.) from JavaScript.
So an analogue of the CSS code: a:hover { color: red }
in JS.
I couldn't find the answer anywhere else; if anyone knows that this is something browsers do not support, that would be a helpful result as well.
This question is related to
javascript
css
pseudo-class
css-in-js
There is another alternative. Instead of manipulating the pseudo-classes directly, create real classes that model the same things, like a "hover" class or a "visited" class. Style the classes with the usual "." syntax and then you can use JavaScript to add or remove classes from an element when the appropriate event fires.
Switching stylesheets in and out is the way to do it. Here is a library to build stylesheets dynamically, so you can set styles on the fly:
http://www.4pmp.com/2009/11/dynamic-css-pseudo-class-styles-with-jquery/
In jquery you can easily set hover pseudo classes.
$("p").hover(function(){
$(this).css("background-color", "yellow");
}, function(){
$(this).css("background-color", "pink");
});
here is a solution including two functions: addCSSclass adds a new css class to the document, and toggleClass turns it on
The example shows adding a custom scrollbar to a div
// If newState is provided add/remove theClass accordingly, otherwise toggle theClass_x000D_
function toggleClass(elem, theClass, newState) {_x000D_
var matchRegExp = new RegExp('(?:^|\\s)' + theClass + '(?!\\S)', 'g');_x000D_
var add = (arguments.length > 2 ? newState : (elem.className.match(matchRegExp) === null));_x000D_
_x000D_
elem.className = elem.className.replace(matchRegExp, ''); // clear all_x000D_
if (add) elem.className += ' ' + theClass;_x000D_
}_x000D_
_x000D_
function addCSSclass(rules) {_x000D_
var style = document.createElement("style");_x000D_
style.appendChild(document.createTextNode("")); // WebKit hack :(_x000D_
document.head.appendChild(style);_x000D_
var sheet = style.sheet;_x000D_
_x000D_
rules.forEach((rule, index) => {_x000D_
try {_x000D_
if ("insertRule" in sheet) {_x000D_
sheet.insertRule(rule.selector + "{" + rule.rule + "}", index);_x000D_
} else if ("addRule" in sheet) {_x000D_
sheet.addRule(rule.selector, rule.rule, index);_x000D_
}_x000D_
} catch (e) {_x000D_
// firefox can break here _x000D_
}_x000D_
_x000D_
})_x000D_
}_x000D_
_x000D_
let div = document.getElementById('mydiv');_x000D_
addCSSclass([{_x000D_
selector: '.narrowScrollbar::-webkit-scrollbar',_x000D_
rule: 'width: 5px'_x000D_
},_x000D_
{_x000D_
selector: '.narrowScrollbar::-webkit-scrollbar-thumb',_x000D_
rule: 'background-color:#808080;border-radius:100px'_x000D_
}_x000D_
]);_x000D_
toggleClass(div, 'narrowScrollbar', true);
_x000D_
<div id="mydiv" style="height:300px;width:300px;border:solid;overflow-y:scroll">_x000D_
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a eros metus. Nunc dui felis, accumsan nec aliquam quis, fringilla quis tellus. Nulla cursus mauris nibh, at faucibus justo tincidunt eget. Sed sodales eget erat consectetur consectetur. Vivamus_x000D_
a diam volutpat, ullamcorper justo eu, dignissim ante. Aenean turpis tortor, fringilla quis efficitur eleifend, iaculis id quam. Quisque non turpis in lacus finibus auctor. Morbi ullamcorper felis ut nulla venenatis fringilla. Praesent imperdiet velit_x000D_
nec sodales sodales. Etiam eget dui sollicitudin, tempus tortor non, porta nibh. Quisque eu efficitur velit. Nulla facilisi. Sed varius a erat ac volutpat. Sed accumsan maximus feugiat. Mauris id malesuada dui. Lorem ipsum dolor sit amet, consectetur_x000D_
adipiscing elit. Sed a eros metus. Nunc dui felis, accumsan nec aliquam quis, fringilla quis tellus. Nulla cursus mauris nibh, at faucibus justo tincidunt eget. Sed sodales eget erat consectetur consectetur. Vivamus a diam volutpat, ullamcorper justo_x000D_
eu, dignissim ante. Aenean turpis tortor, fringilla quis efficitur eleifend, iaculis id quam. Quisque non turpis in lacus finibus auctor. Morbi ullamcorper felis ut nulla venenatis fringilla. Praesent imperdiet velit nec sodales sodales. Etiam eget_x000D_
dui sollicitudin, tempus tortor non, porta nibh. Quisque eu efficitur velit. Nulla facilisi. Sed varius a erat ac volutpat. Sed accumsan maximus feugiat. Mauris id malesuada dui._x000D_
</div>
_x000D_
I threw together a small library for this since I do think there are valid use cases for manipulating stylesheets in JS. Reasons being:
Just place the css in a template string.
const cssTemplateString = `.foo:[psuedoSelector]{prop: value}`;
Then create a style element and place the string in the style tag and attach it to the document.
const styleTag = document.createElement("style");
styleTag.innerHTML = cssTemplateString;
document.head.insertAdjacentElement('beforeend', styleTag);
Specificity will take care of the rest. Then you can remove and add style tags dynamically. This is a simple alternative to libraries and messing with the stylesheet array in the DOM. Happy Coding!
Switching stylesheets in and out is the way to do it. Here is a library to build stylesheets dynamically, so you can set styles on the fly:
http://www.4pmp.com/2009/11/dynamic-css-pseudo-class-styles-with-jquery/
I threw together a small library for this since I do think there are valid use cases for manipulating stylesheets in JS. Reasons being:
Instead of directly setting pseudo-class rules with javascript, you can set the rules differently in different CSS files, and then use Javascript to switch one stylesheet off and to switch another on. A method is described at A List Apart (qv. for more detail).
Set up the CSS files as,
<link rel="stylesheet" href="always_on.css">
<link rel="stylesheet" title="usual" href="preferred.css"> <!-- on by default -->
<link rel="alternate stylesheet" title="strange" href="alternate.css"> <!-- off by default -->
And then switch between them using javascript:
function setActiveStyleSheet(title) {
var i, a, main;
for(i=0; (a = document.getElementsByTagName("link")<i>); i++) {
if(a.getAttribute("rel").indexOf("style") != -1
&& a.getAttribute("title")) {
a.disabled = true;
if(a.getAttribute("title") == title) a.disabled = false;
}
}
}
A function to cope with the cross-browser stuff:
addCssRule = function(/* string */ selector, /* string */ rule) {
if (document.styleSheets) {
if (!document.styleSheets.length) {
var head = document.getElementsByTagName('head')[0];
head.appendChild(bc.createEl('style'));
}
var i = document.styleSheets.length-1;
var ss = document.styleSheets[i];
var l=0;
if (ss.cssRules) {
l = ss.cssRules.length;
} else if (ss.rules) {
// IE
l = ss.rules.length;
}
if (ss.insertRule) {
ss.insertRule(selector + ' {' + rule + '}', l);
} else if (ss.addRule) {
// IE
ss.addRule(selector, rule, l);
}
}
};
As already stated this is not something that browsers support.
If you aren't coming up with the styles dynamically (i.e. pulling them out of a database or something) you should be able to work around this by adding a class to the body of the page.
The css would look something like:
a:hover { background: red; }
.theme1 a:hover { background: blue; }
And the javascript to change this would be something like:
// Look up some good add/remove className code if you want to do this
// This is really simplified
document.body.className += " theme1";
My trick is using an attribute selector. Attributes are easier to set up by javascript.
css
.class{ /*normal css... */}
.class[special]:after{ content: 'what you want'}
javascript
function setSpecial(id){ document.getElementById(id).setAttribute('special', '1'); }
html
<element id='x' onclick="setSpecial(this.id)"> ...
As already stated this is not something that browsers support.
If you aren't coming up with the styles dynamically (i.e. pulling them out of a database or something) you should be able to work around this by adding a class to the body of the page.
The css would look something like:
a:hover { background: red; }
.theme1 a:hover { background: blue; }
And the javascript to change this would be something like:
// Look up some good add/remove className code if you want to do this
// This is really simplified
document.body.className += " theme1";
You can't style a pseudo-class on a particular element alone, in the same way that you can't have a pseudo-class in an inline style="..." attribute (as there is no selector).
You can do it by altering the stylesheet, for example by adding the rule:
#elid:hover { background: red; }
assuming each element you want to affect has a unique ID to allow it to be selected.
In theory the document you want is http://www.w3.org/TR/DOM-Level-2-Style/Overview.html which means you can (given a pre-existing embedded or linked stylesheet) using syntax like:
document.styleSheets[0].insertRule('#elid:hover { background-color: red; }', 0);
document.styleSheets[0].cssRules[0].style.backgroundColor= 'red';
IE, of course, requires its own syntax:
document.styleSheets[0].addRule('#elid:hover', 'background-color: red', 0);
document.styleSheets[0].rules[0].style.backgroundColor= 'red';
Older and minor browsers are likely not to support either syntax. Dynamic stylesheet-fiddling is rarely done because it's quite annoying to get right, rarely needed, and historically troublesome.
A function to cope with the cross-browser stuff:
addCssRule = function(/* string */ selector, /* string */ rule) {
if (document.styleSheets) {
if (!document.styleSheets.length) {
var head = document.getElementsByTagName('head')[0];
head.appendChild(bc.createEl('style'));
}
var i = document.styleSheets.length-1;
var ss = document.styleSheets[i];
var l=0;
if (ss.cssRules) {
l = ss.cssRules.length;
} else if (ss.rules) {
// IE
l = ss.rules.length;
}
if (ss.insertRule) {
ss.insertRule(selector + ' {' + rule + '}', l);
} else if (ss.addRule) {
// IE
ss.addRule(selector, rule, l);
}
}
};
One option you could consider is using CSS variables. The idea is that you set the property you want to change to a CSS variable. Then, within your JS, change that variable's value.
See example below
function changeColor(newColor) {
document.documentElement.style.setProperty("--anchor-hover-color", newColor);
// ^^^^^^^^^^^-- select the root
}
_x000D_
:root {
--anchor-hover-color: red;
}
a:hover {
color: var(--anchor-hover-color);
}
_x000D_
<a href="#">Hover over me</a>
<button onclick="changeColor('lime')">Change to lime</button>
<button onclick="changeColor('red')">Change to red</button>
_x000D_
You can't style a pseudo-class on a particular element alone, in the same way that you can't have a pseudo-class in an inline style="..." attribute (as there is no selector).
You can do it by altering the stylesheet, for example by adding the rule:
#elid:hover { background: red; }
assuming each element you want to affect has a unique ID to allow it to be selected.
In theory the document you want is http://www.w3.org/TR/DOM-Level-2-Style/Overview.html which means you can (given a pre-existing embedded or linked stylesheet) using syntax like:
document.styleSheets[0].insertRule('#elid:hover { background-color: red; }', 0);
document.styleSheets[0].cssRules[0].style.backgroundColor= 'red';
IE, of course, requires its own syntax:
document.styleSheets[0].addRule('#elid:hover', 'background-color: red', 0);
document.styleSheets[0].rules[0].style.backgroundColor= 'red';
Older and minor browsers are likely not to support either syntax. Dynamic stylesheet-fiddling is rarely done because it's quite annoying to get right, rarely needed, and historically troublesome.
As already stated this is not something that browsers support.
If you aren't coming up with the styles dynamically (i.e. pulling them out of a database or something) you should be able to work around this by adding a class to the body of the page.
The css would look something like:
a:hover { background: red; }
.theme1 a:hover { background: blue; }
And the javascript to change this would be something like:
// Look up some good add/remove className code if you want to do this
// This is really simplified
document.body.className += " theme1";
If you use REACT , There is something called radium. It is so useful here :
Add handlers to props if interactive styles are specified, e.g. onMouseEnter for :hover, wrapping existing handlers if necessary
If any of the handlers are triggered, e.g. by hovering, Radium calls setState to update a Radium-specific field on the components state object
On re-render, resolve any interactive styles that apply, e.g. :hover, by looking up the element's key or ref in the Radium-specific state
My trick is using an attribute selector. Attributes are easier to set up by javascript.
css
.class{ /*normal css... */}
.class[special]:after{ content: 'what you want'}
javascript
function setSpecial(id){ document.getElementById(id).setAttribute('special', '1'); }
html
<element id='x' onclick="setSpecial(this.id)"> ...
One option you could consider is using CSS variables. The idea is that you set the property you want to change to a CSS variable. Then, within your JS, change that variable's value.
See example below
function changeColor(newColor) {
document.documentElement.style.setProperty("--anchor-hover-color", newColor);
// ^^^^^^^^^^^-- select the root
}
_x000D_
:root {
--anchor-hover-color: red;
}
a:hover {
color: var(--anchor-hover-color);
}
_x000D_
<a href="#">Hover over me</a>
<button onclick="changeColor('lime')">Change to lime</button>
<button onclick="changeColor('red')">Change to red</button>
_x000D_
As already stated this is not something that browsers support.
If you aren't coming up with the styles dynamically (i.e. pulling them out of a database or something) you should be able to work around this by adding a class to the body of the page.
The css would look something like:
a:hover { background: red; }
.theme1 a:hover { background: blue; }
And the javascript to change this would be something like:
// Look up some good add/remove className code if you want to do this
// This is really simplified
document.body.className += " theme1";
In jquery you can easily set hover pseudo classes.
$("p").hover(function(){
$(this).css("background-color", "yellow");
}, function(){
$(this).css("background-color", "pink");
});
You can't style a pseudo-class on a particular element alone, in the same way that you can't have a pseudo-class in an inline style="..." attribute (as there is no selector).
You can do it by altering the stylesheet, for example by adding the rule:
#elid:hover { background: red; }
assuming each element you want to affect has a unique ID to allow it to be selected.
In theory the document you want is http://www.w3.org/TR/DOM-Level-2-Style/Overview.html which means you can (given a pre-existing embedded or linked stylesheet) using syntax like:
document.styleSheets[0].insertRule('#elid:hover { background-color: red; }', 0);
document.styleSheets[0].cssRules[0].style.backgroundColor= 'red';
IE, of course, requires its own syntax:
document.styleSheets[0].addRule('#elid:hover', 'background-color: red', 0);
document.styleSheets[0].rules[0].style.backgroundColor= 'red';
Older and minor browsers are likely not to support either syntax. Dynamic stylesheet-fiddling is rarely done because it's quite annoying to get right, rarely needed, and historically troublesome.
If you use REACT , There is something called radium. It is so useful here :
Add handlers to props if interactive styles are specified, e.g. onMouseEnter for :hover, wrapping existing handlers if necessary
If any of the handlers are triggered, e.g. by hovering, Radium calls setState to update a Radium-specific field on the components state object
On re-render, resolve any interactive styles that apply, e.g. :hover, by looking up the element's key or ref in the Radium-specific state
You can't style a pseudo-class on a particular element alone, in the same way that you can't have a pseudo-class in an inline style="..." attribute (as there is no selector).
You can do it by altering the stylesheet, for example by adding the rule:
#elid:hover { background: red; }
assuming each element you want to affect has a unique ID to allow it to be selected.
In theory the document you want is http://www.w3.org/TR/DOM-Level-2-Style/Overview.html which means you can (given a pre-existing embedded or linked stylesheet) using syntax like:
document.styleSheets[0].insertRule('#elid:hover { background-color: red; }', 0);
document.styleSheets[0].cssRules[0].style.backgroundColor= 'red';
IE, of course, requires its own syntax:
document.styleSheets[0].addRule('#elid:hover', 'background-color: red', 0);
document.styleSheets[0].rules[0].style.backgroundColor= 'red';
Older and minor browsers are likely not to support either syntax. Dynamic stylesheet-fiddling is rarely done because it's quite annoying to get right, rarely needed, and historically troublesome.
There is another alternative. Instead of manipulating the pseudo-classes directly, create real classes that model the same things, like a "hover" class or a "visited" class. Style the classes with the usual "." syntax and then you can use JavaScript to add or remove classes from an element when the appropriate event fires.
Instead of directly setting pseudo-class rules with javascript, you can set the rules differently in different CSS files, and then use Javascript to switch one stylesheet off and to switch another on. A method is described at A List Apart (qv. for more detail).
Set up the CSS files as,
<link rel="stylesheet" href="always_on.css">
<link rel="stylesheet" title="usual" href="preferred.css"> <!-- on by default -->
<link rel="alternate stylesheet" title="strange" href="alternate.css"> <!-- off by default -->
And then switch between them using javascript:
function setActiveStyleSheet(title) {
var i, a, main;
for(i=0; (a = document.getElementsByTagName("link")<i>); i++) {
if(a.getAttribute("rel").indexOf("style") != -1
&& a.getAttribute("title")) {
a.disabled = true;
if(a.getAttribute("title") == title) a.disabled = false;
}
}
}
Just place the css in a template string.
const cssTemplateString = `.foo:[psuedoSelector]{prop: value}`;
Then create a style element and place the string in the style tag and attach it to the document.
const styleTag = document.createElement("style");
styleTag.innerHTML = cssTemplateString;
document.head.insertAdjacentElement('beforeend', styleTag);
Specificity will take care of the rest. Then you can remove and add style tags dynamically. This is a simple alternative to libraries and messing with the stylesheet array in the DOM. Happy Coding!
Source: Stackoverflow.com