How to get css pseudo-element with Javascript? [duplicate] - javascript

The function window.getComputedStyle is supposed to be able to get the computed style of pseudo classes like :hover, according to the documentation.
It's also explained as an answer in another question
But as the last comment says in that question, in fact it doesn't work at all, it just returns the normal style, not the :hover style. You can see for yourself in this jsfiddle. The alert returns the red color, not the green one.
The documentation on developer.mozilla.org also has an example, but that doesn't work either - see here.
In this question the answerer states in a comment that it won't work at all, but without giving an explanation.
Could it be that the stylesheet has to be fully rendered before the function returns the correct value? I've tried setting some delays, but nothing seems to work.
I've tried the latest Firefox, Chrome and IE browsers. Does anybody know why this function is not working as expected?

var style = window.getComputedStyle(element[, pseudoElt]);
where pseudoElt is "a string specifying the pseudo-element to match". A pseudo-element is something like ::before or ::after, those are elements which are generated by CSS styles. :hover, :visited or other similar effects are pseuodo-classes. They won't create new elements but apply specialized class styles to the element.
It's not possible to get the computed style of a pseudo-class, at least not with getComputedStyle. You can however traverse through the CSS rules and try to find a fitting selector, but I won't recommend you to do this, since some browsers won't follow the DOM-Level-2-Style rules and you would have to check which rule will affect your specific element:
/* Style */
p a:hover{ color:yellow; background:black;}
p > a:hover{ color:green; background:white;}
p > a+a:hover{ color:fuchsia; background:blue;}
// JS
var i,j, sel = /a:hover/;
for(i = 0; i < document.styleSheets.length; ++i){
for(j = 0; j < document.styleSheets[i].cssRules.length; ++j){
if(sel.test(document.styleSheets[i].cssRules[j].selectorText)){
console.log(
document.styleSheets[i].cssRules[j].selectorText,
document.styleSheets[i].cssRules[j].style.cssText
);
}
}
}
Result:
p a:hover color: yellow; background: none repeat scroll 0% 0% black;
p > a:hover color: green; background: none repeat scroll 0% 0% white;
p > a + a:hover color: fuchsia; background: none repeat scroll 0% 0% blue;
See also:
MDN: getComputedStyle examples
W3C: CSS2: 5.10 Pseudo-elements and pseudo-classes
SO: Setting CSS pseudo-class rules from JavaScript

Note that there is a way to do this at least for Chrome with the remote debugging port turned on. Basically you need to use a WebSocket, connect to the remote debugger url, then issue the following commands:
send { id: 1, method: "Inspector.enable" }
send { id: 2, method: "DOM.enable" }
send { id: 3, method: "CSS.enable" }
send { id: 4, method: "DOM.getDocument" }
send { id: 5, method: "DOM.querySelector", params: { nodeId: 1, selector: <<whatever you want>> } }
nodeId = response.result.nodeId
send { id: 6, method: "CSS.forcePseudoState", params: { nodeId:, forcedPseudoClasses: [ "hover" <<or whatever>> ] } }
Then you can use the regular getComputedStyle, or the CSS.getComputedStyleForNode debugger API.

Related

How to call access pseudo elements styling from Javascript? [duplicate]

This question already has answers here:
Selecting and manipulating CSS pseudo-elements such as ::before and ::after using javascript (or jQuery)
(26 answers)
Closed 6 years ago.
Earlier my question was:-
I have the following code in my Sass css file
.random {
box-sizing: content-box;
text-align: center;
line-height: 1;
&:before {
display: inline-block;
margin-right: 0.2em;
width: 0;
height: 0;
border-right: 0.4em solid transparent;
border-left: 0.4em solid transparent;
content: "";
vertical-align: baseline;
}
}
.perc-neg:before {
border-top: 0.5em solid #FCB062;
}
.perc-neg.good:before {
border-top: 0.5em solid #98F1AC;
}
I have a div with
class = "random perc-neg good"
Now I want to change style of the above div. how to do it?
I tried following in console but it returns empty object
$("random perc-neg good:before").css("color","red");
$("random.perc-neg.good:before").css("color","red");
$(".random.perc-neg.good:before").css("color","red");
Someone has suggested its a possible duplicate but its not.
Int he related question, the user just wanted to make it visible or hidden so two classes will be sufficient.
But my requirement is to change the color as per user's choice which he can select from wide range of colors.
Its definitely impossible to define a class with each color changes.
And we cant pass some variable to css as well to change the color property accordingly.
Updated Question:
I am now using Sass.
I have defined an function to update the color
#function em($color) {
#return border-bottom: 0.5em solid $color;
}
.perc-neg.good:before {
em(#98F1AC);
}
definitely, I can call the function from the Sass file but how to call it from javascript
Now I want to pass the hex code of color from javascript
I need to pass something like this from javascript
.perc-neg.good:before(#98F1AC)
looked for the same in google did not find anything relevant
Instead of marking it as duplicate, it would be much better if you can provide a solution
You cannot access pseudo elements in Javascript since these elements are not in the DOM.
If you want the change pseudo elements styling, you need to predefine css classes for that purpose and add/remove those based on your triggering events.
If that is not possible, simply don't set the colorproperty on the pseudo element at all, but on the host element, since :before and :after will then inherit their host element's color property (if they don't have a specific color property assigned to themselves in CSS).
You cannot call a SASS / LESS function from javascript as they are both pre-compilers that just produce a static stylesheet.
If you have a limited color pallet you could create all the rules that cover your use cases.
However you do have the ability to create a style element with javascript and append new rules to it. Here is a simple example that you could expand on
// add Style is wrapped in a closure that locks in a single
// style element that you can add to on the fly
const addStyle = (() => {
// create and append the style element
const styleSheet = document.createElement('style')
document.head.appendChild(styleSheet)
const style = styleSheet.sheet
// helper function to serialize an object to a css string
const serializeStyle = (styles) => {
return Object.keys(styles).reduce((str, prop) => {
return str + kebabCase(prop) + ':' + styles[prop] + ';'
}, '')
}
// helper function to convert camelCase to kebab-case
const kebabCase = (str) =>
str.replace(/([A-Z])/g, (_, letter) => '-'+ letter.toUpperCase())
// return the public function
return (selector, styles) => {
// add a new rule to the created stylesheet
style.insertRule(
selector + '{' + serializeStyle(styles) + '}',
style.cssRules.length
)
}
})()
addStyle('.random::before', {
border: '10px solid #aec369'
})
addStyle('.random::before', {
background: '#bada55',
boxShadow: '5px 5px 10px #777'
})
const el = document.querySelector('#color')
const em = () => {
addStyle('.random::before', {
borderColor: el.value
})
}
el.addEventListener('keyup', em)
el.addEventListener('change', em)
.random {
position: relative;
background-color: #eee;
}
.random::before {
content: '';
display: block;
height: 200px;
width: 200px;
}
<input id="color" placeholder="#000000" />
<div class="random">
</div>

How to get the "declared" CSS value, not the computed CSS value

How can we use JavaScript to get the value of the declared CSS value (like the value that shows up when inspecting an element using Chrome's inspector? Everything I try and search for on the web seems to only want to give the computed value.
For my specific use case I want to know what the width will be at the end of a CSS transition, which is set in the stylesheet, not inline. When I check for the width using JavaScript, I get the computed width, which at the time of retrieval in the script is at the beginning of the transition, so shows me 0 even though in 300ms it will be a declared width like 320px.
Take a look at transitionend event. Can it be used for your use case?
EDIT
Since this answer got upvoted i'm pasting some code below.
element.addEventListener("transitionend", function() {
// get CSS property here
}, false);
I think this is what you're after:
$('#widen').on('click', function(e){
$('.example').addClass('wider');
$('#prefetch').text('The div will be: ' + getWidth('wider'));
});
function getWidth(className){
for (var s = 0; s < document.styleSheets.length; s++){
var rules = document.styleSheets[s].rules || document.styleSheets[s].cssRules; console.log(rules);
for (var r = 0; r < rules.length; r++){
var rule = rules[r]; console.log(rule);
if (rule.selectorText == '.'+className){
console.log('match!');
return rule.style.width;
}
}
}
return undefined;
}
.example {
width: 100px;
background-color: #ccc;
border: 1px solid black;
}
.wider {
width: 320px;
-webkit-transition: width 5s;
-moz-transition: width 5s;
-o-transition: width 5s;
transition: width 5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="example">This is a simple container.</div>
<button id="widen" type="button">Widen</button>
<span id="prefetch"></span>
Keep in mind, I believe this still falls victim to cross-domain preventions (if the CSS is hosted on a CDN/other domain, you won't be able to retrieve the styleSheet, and, therefore, not be able to access then eventual width.
I selected the most helpful answer to my question, but it appears that the solution I was looking for does not exist. What I needed was to get what the width of an element would be in pixels BEFORE the transition actually was completed. This width is percent based and so the actual pixels width would vary based on a number of factors. What I ended up doing in reality was:
Making a jQuery clone of the item of which I needed the "end"
transition measurement.
Positioning the clone off screen
Adding inline styles to the clone, remove the CSS inherited transition properties so that it immediately gets the final/end width.
Using JS to save the ending width to a variable
Removing the clone with jQuery's .remove()
Doing this, I now know what the ending width in pixels would be at the moment the element begins to transition, rather than having to wait until the end of the transition to capture the ending width.
Here is a function that does what I described above.
var getTransitionEndWidth = function($el) {
$('body').append('<div id="CopyElWrap" style="position:absolute; top:-9999px; opacity:0;"></div>');
var copyEl = document.createElement('div');
var $copy = $(copyEl);
var copyElClasses = $el.attr('class');
$copy.attr('class', copyElClasses).css({
WebkitTransition: 'all 0 0',
MozTransition: 'all 0 0',
MsTransition: 'all 0 0',
OTransition: 'all 0 0',
transition: 'all 0 0'
}).appendTo($('#CopyElWrap'));
var postWidth = $copy.width();
$('#CopyElWrap').remove();
return postWidth;
};

Dynamically changing less variables

I want to change a less variable on client side.
Say I have a less file
#color1: #123456;
#color2: #color1 + #111111;
.title { color: #color1; }
.text { color: #color2; }
I want that user yo pick a color and change the value of #color1 and recompile css without reloading the page.
Basically I'm looking for a js function, something like this
less_again({color1: '#ff0000'})
Marvin,
I wrote a function that does exactly what you're looking for, last night.
I have created a fork on Github;
https://github.com/hbi99/less.js/commit/6508fe89a6210ae3cd8fffb8e998334644e7dcdc
Take a look at it. Since this is a recent addition, I'd like to hear your comments on the addition. This solution fits my needs perfectly and I think it will do the same for you.
Here is a basic sample;
Sample LESS:
#bgColor: black;
#textColor: yellow;
body {background: #bgColor; color: #textColor;}
From JS:
less.modifyVars({
'#bgColor': 'blue',
'#textColor': 'red'
});
The creator of less.js added some features that should allow you to do something like this. Read the comments and the answers here: Load less.js rules dynamically
This less:
#color1: #123456;
#color2: #color1 + #111111;
.title { color: #color1; }
.text { color: #color2; }
compiles to this CSS and this is all the browser knows about:
.title { color: #123456; }
.text { color: #234567; }
So, now you're effectively saying you want to change dynamically to this:
.title { color: #ff0000; }
You can do that by reaching into the existing stylesheet with JS and changing the rule for .title. Or, you can define an alternate rule in your original CSS:
.alternate.title { color: #ff0000; }
And, find all the objects with .title and add .alternate to them. In jQuery, this would be as simple as:
$(".title").addClass(".alternate");
In plain JS, you'd need to use a shim to provide getElementsByClassName in a cross browser fashion and then use:
var list = document.getElementsByClassName("title");
for (var i = 0, len = list.length; i < len; i++) {
list[i].className += " alternate";
}

Change text selection highlight with JS

For standard browsers you can use something like this to change the coloring of selected text:
div.txtArea::selection {
background: transparent;
}
div.txtArea::-moz-selection {
background: transparent;
}
div.txtArea::-webkit-selection {
background: transparent;
}
But I need to do this with JavaScript instead.
My users can select text and then change the color. While they are selecting another color it updates the color constantly. Since the text is selected they can't see what the color looks like. I need to change the selection style of my targeted element to be transparent only during mouseover of the color changer.
I have tried a few things including:
$('div.txtArea').css({
'selection': 'transparent',
'-moz-selection': 'transparent',
'-webkit-selection': 'transparent'
});
Is there a way to do this with javascript?
There's no DOM interface for manipulating pseudo-classes. The only thing you can do is add the rules to a stylesheet. For instance:
// Get the first stylesheet
var ss = document.styleSheets[0]
// Use insertRule() for standards, addRule() for IE
if ("insertRule" in ss) {
ss.insertRule('div.txtArea::-moz-selection { background: transparent; }', 0);
ss.insertRule('div.txtArea::selection { background: transparent; }', 0);
ss.insertRule('div.txtArea::-webkit-selection { background: transparent; }', 0);
}
You can access and change rules using stylesheet.cssRules[index].style, stylesheet.rules[index].style for IE, which is where it gets a little more complicated.
I didn't include an IE6-8 example using addRule() because those versions of IE don't support ::selection.

Wait cursor over entire html page

Is it possible to set the cursor to 'wait' on the entire html page in a simple way? The idea is to show the user that something is going on while an ajax call is being completed. The code below shows a simplified version of what I tried and also demonstrate the problems I run into:
if an element (#id1) has a cursor style set it will ignore the one set on body (obviously)
some elements have a default cursor style (a) and will not show the wait cursor on hover
the body element has a certain height depending on the content and if the page is short, the cursor will not show below the footer
The test:
<html>
<head>
<style type="text/css">
#id1 {
background-color: #06f;
cursor: pointer;
}
#id2 {
background-color: #f60;
}
</style>
</head>
<body>
<div id="id1">cursor: pointer</div>
<div id="id2">no cursor</div>
Do something
</body>
</html>
Later edit...
It worked in firefox and IE with:
div#mask { display: none; cursor: wait; z-index: 9999;
position: absolute; top: 0; left: 0; height: 100%;
width: 100%; background-color: #fff; opacity: 0; filter: alpha(opacity = 0);}
<a href="#" onclick="document.getElementById('mask').style.display = 'block'; return false">
Do something</a>
The problem with (or feature of) this solution is that it will prevent clicks because of the overlapping div (thanks Kibbee)
Later later edit...
A simpler solution from Dorward:
.wait, .wait * { cursor: wait !important; }
and then
Do something
This solution only shows the wait cursor but allows clicks.
If you use this slightly modified version of the CSS you posted from Dorward,
html.wait, html.wait * { cursor: wait !important; }
you can then add some really simple jQuery to work for all ajax calls:
$(document).ready(function () {
$(document).ajaxStart(function () { $("html").addClass("wait"); });
$(document).ajaxStop(function () { $("html").removeClass("wait"); });
});
or, for older jQuery versions (before 1.9):
$(document).ready(function () {
$("html").ajaxStart(function () { $(this).addClass("wait"); });
$("html").ajaxStop(function () { $(this).removeClass("wait"); });
});
I understand you may not have control over this, but you might instead go for a "masking" div that covers the entire body with a z-index higher than 1. The center part of the div could contain a loading message if you like.
Then, you can set the cursor to wait on the div and don't have to worry about links as they are "under" your masking div. Here's some example CSS for the "masking div":
body { height: 100%; }
div#mask { cursor: wait; z-index: 999; height: 100%; width: 100%; }
This seems to work in firefox
<style>
*{ cursor: inherit;}
body{ cursor: wait;}
</style>
The * part ensures that the cursor doesn't change when you hover over a link. Although links will still be clickable.
I have been struggling with this problem for hours today.
Basically everything was working just fine in FireFox but (of course) not in IE.
In IE the wait cursor was showing AFTER the time consuming function was executed.
I finally found the trick on this site:
http://www.codingforums.com/archive/index.php/t-37185.html
Code:
//...
document.body.style.cursor = 'wait';
setTimeout(this.SomeLongFunction, 1);
//setTimeout syntax when calling a function with parameters
//setTimeout(function() {MyClass.SomeLongFunction(someParam);}, 1);
//no () after function name this is a function ref not a function call
setTimeout(this.SetDefaultCursor, 1);
...
function SetDefaultCursor() {document.body.style.cursor = 'default';}
function SomeLongFunction(someParam) {...}
My code runs in a JavaScript class hence the this and MyClass (MyClass is a singleton).
I had the same problems when trying to display a div as described on this page. In IE it was showing after the function had been executed. So I guess this trick would solve that problem too.
Thanks a zillion time to glenngv the author of the post. You really made my day!!!
Easiest way I know is using JQuery like this:
$('*').css('cursor','wait');
css: .waiting * { cursor: 'wait' }
jQuery: $('body').toggleClass('waiting');
Why don't you just use one of those fancy loading graphics (eg: http://ajaxload.info/)? The waiting cursor is for the browser itself - so whenever it appears it has something to do with the browser and not with the page.
To set the cursor from JavaScript for the whole window, use:
document.documentElement.style.cursor = 'wait';
From CSS:
html { cursor: wait; }
Add further logic as needed.
Try the css:
html.waiting {
cursor: wait;
}
It seems that if the property body is used as apposed to html it doesn't show the wait cursor over the whole page. Furthermore if you use a css class you can easily control when it actually shows it.
Here is a more elaborate solution that does not require external CSS:
function changeCursor(elem, cursor, decendents) {
if (!elem) elem=$('body');
// remove all classes starting with changeCursor-
elem.removeClass (function (index, css) {
return (css.match (/(^|\s)changeCursor-\S+/g) || []).join(' ');
});
if (!cursor) return;
if (typeof decendents==='undefined' || decendents===null) decendents=true;
let cname;
if (decendents) {
cname='changeCursor-Dec-'+cursor;
if ($('style:contains("'+cname+'")').length < 1) $('<style>').text('.'+cname+' , .'+cname+' * { cursor: '+cursor+' !important; }').appendTo('head');
} else {
cname='changeCursor-'+cursor;
if ($('style:contains("'+cname+'")').length < 1) $('<style>').text('.'+cname+' { cursor: '+cursor+' !important; }').appendTo('head');
}
elem.addClass(cname);
}
with this you can do:
changeCursor(, 'wait'); // wait cursor on all decendents of body
changeCursor($('#id'), 'wait', false); // wait cursor on elem with id only
changeCursor(); // remove changed cursor from body
I used a adaptation of Eric Wendelin's solution. It will show a transparent, animated overlay wait-div over the whole body, the click will be blocked by the wait-div while visible:
css:
div#waitMask {
z-index: 999;
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 100%;
cursor: wait;
background-color: #000;
opacity: 0;
transition-duration: 0.5s;
-webkit-transition-duration: 0.5s;
}
js:
// to show it
$("#waitMask").show();
$("#waitMask").css("opacity"); // must read it first
$("#waitMask").css("opacity", "0.8");
...
// to hide it
$("#waitMask").css("opacity", "0");
setTimeout(function() {
$("#waitMask").hide();
}, 500) // wait for animation to end
html:
<body>
<div id="waitMask" style="display:none;"> </div>
... rest of html ...
My Two pence:
Step 1:
Declare an array. This will be used to store the original cursors that were assigned:
var vArrOriginalCursors = new Array(2);
Step 2:
Implement the function cursorModifyEntirePage
function CursorModifyEntirePage(CursorType){
var elements = document.body.getElementsByTagName('*');
alert("These are the elements found:" + elements.length);
let lclCntr = 0;
vArrOriginalCursors.length = elements.length;
for(lclCntr = 0; lclCntr < elements.length; lclCntr++){
vArrOriginalCursors[lclCntr] = elements[lclCntr].style.cursor;
elements[lclCntr].style.cursor = CursorType;
}
}
What it does:
Gets all the elements on the page. Stores the original cursors assigned to them in the array declared in step 1. Modifies the cursors to the desired cursor as passed by parameter CursorType
Step 3:
Restore the cursors on the page
function CursorRestoreEntirePage(){
let lclCntr = 0;
var elements = document.body.getElementsByTagName('*');
for(lclCntr = 0; lclCntr < elements.length; lclCntr++){
elements[lclCntr].style.cursor = vArrOriginalCursors[lclCntr];
}
}
I have run this in an application and it works fine.
Only caveat is that I have not tested it when you are dynamically adding the elements.
BlockUI is the answer for everything. Give it a try.
http://www.malsup.com/jquery/block/
This pure JavaScript seems to work pretty well ... tested on FireFox, Chrome, and Edge browsers.
I'm not sure about the performance of this if you had an overabundance of elements on your page and a slow computer ... try it and see.
Set cursor for all elements to wait:
Object.values(document.querySelectorAll('*')).forEach(element => element.style.cursor = "wait");
Set cursor for all elements back to default:
Object.values(document.querySelectorAll('*')).forEach(element => element.style.cursor = "default");
An alternative (and perhaps a bit more readable) version would be to create a setCursor function as follows:
function setCursor(cursor)
{
var x = document.querySelectorAll("*");
for (var i = 0; i < x.length; i++)
{
x[i].style.cursor = cursor;
}
}
and then call
setCursor("wait");
and
setCursor("default");
to set the wait cursor and default cursor respectively.
Lots of good answers already, but none of them mentions the <dialog> element.
Using this element we can create a solution similar to the masking <div>.
Here we use showModal() to "hide" elements, and we use ::backdrop to set the cursor style to wait on the entire page:
function showWaitDialog() {
document.getElementById('id_dialog').showModal();
}
#id_dialog, #id_dialog::backdrop {
cursor: wait;
}
<button onclick="showWaitDialog()">click me</button>
<dialog id="id_dialog">busy...</dialog>
The dialog is hidden by default, and can be shown using either the show() method, or the showModal() method, which prevents clicking outside the dialog.
The dialog can be forced to close using the close() method, if necessary.
However, if your button links to another page, for example, then the dialog will disappear automatically as soon as the new page is loaded.
Note that the dialog can also be closed at any time by hitting the Esc key.
CSS can be used to style the dialog however you like.
The example uses the html onclick attribute, just for simplicity. Obviously, addEventListener() could also be used.
Late to the party but simply give the Html tag an id by targeting
document.documentElement
and in the CSS place at the top
html#wait * {
cursor: wait !important;
}
and simply remove it when you want to stop this cursor.

Categories

Resources