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

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>

Related

Apply css for pseudo elements using 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 2 years ago.
How I can change CSS for a pseudo element style?
I am trying to get the CSS before:: rule and change left: to 95% or 4px.
How can I perform this in my context?
I've also made some test using document.querySelector but it doesn't work, I get a compute Read-Only error.
Do you have suggestions?
Example:
css
.iziToast-wrapper-bottomLeft .iziToast.iziToast-balloon:before {
border-right: 15px solid transparent;
border-left: 0 solid transparent;
right: auto;
left: 8px;
}
js
if(description_iziToast){
let RightMode = event.x>window.innerWidth/2;
let bubblePosition = document.getElementsByClassName("iziToast-balloon")[0]; // get the div that hold the bubble
let ajustScreenLR = RightMode && document.getElementsByClassName("iziToast")[0].offsetWidth || 0; // halfScreen ajustement
//bubblePosition.style.left = RightMode && '95%' || '4px'; // here i need to change the position in the befor:: attribut
description_iziToast.style.left = `${event.x-20-ajustScreenLR}px`;
description_iziToast.style.top = `${event.y-105}px`;
}
}else{
if(description_iziToast){ iziToast.destroy(); description_iziToast = false; };
}
Here the app console debug
Since pseudo-elements do not exist in the DOM, they cannot be accessed in Javascript.
The workaround is to create a <span> instead of using :before and the same logic has to be applied.
Here are two ways to directly manipulate a pseudo-element:
First way, is by using some sort of style manager.
This "manager" is an object with methods which allows easier manipulation of CSS rules on-the-fly, so here is a very basic example which you can study and implement for your specific needs:
var elm = document.querySelector('main');
// Reference: https://stackoverflow.com/a/28930990/104380
var styleManager = (function() {
// Create the <style> tag
var style = document.createElement("style")
// WebKit hack
style.appendChild(document.createTextNode(""));
// Add the <style> element to the page
document.head.appendChild(style);
function getStyleRuleIndexBySelector(selector, prop){
var result = [], i,
value = (prop ? selector + "{" + prop + "}" : selector).replace(/\s/g, ''), // remove whitespaces
s = prop ? "cssText" : "selectorText";
for( i=0; i < style.sheet.cssRules.length; i++ )
if( style.sheet.cssRules[i][s].replace(/\s/g, '') == value)
result.push(i);
return result;
};
return {
style : style,
getStyleRuleIndexBySelector : getStyleRuleIndexBySelector,
add(prop, value){
return style.sheet.insertRule(`${prop}{${value}}`, style.sheet.cssRules.length);
},
remove(selector, prop){
var indexes = getStyleRuleIndexBySelector(selector, prop), i = indexes.length;
// reversed iteration so indexes won't change after deletion for each iteration
for( ; i-- ; )
style.sheet.deleteRule( indexes[i] );
}
}
})();
elm.addEventListener('mouseenter', function(){
// each new rule should be added the END of the sheet
styleManager.add('main::before','left:90%; top:60%;');
styleManager.add('main::before','left:70%;');
});
elm.addEventListener('mouseleave', function(){
styleManager.remove('main::before', 'left:70%;'); // you can also try without the "left:70%;" part
});
main{
position:relative;
width: 200px;
height: 200px;
border: 1px dashed silver;
}
main::before{
content: 'pseudo';
width: 100px;
height: 100px;
background: lightgreen;
position: absolute;
left: 10px;
top: 40px;
transition:.3s ease-out;
}
<main>Hover & out</main>
Another way - with CSS variables:
var elm = document.querySelector('main');
elm.addEventListener('mouseenter', function(){
elm.style.setProperty('--before-left', '90%');
});
elm.addEventListener('mouseleave', function(){
elm.style.setProperty('--before-left', '10px');
});
main{
--before-left : 10px; /* <-- Your CSS should use variables for this to work */
position:relative;
width: 200px;
height: 200px;
border: 1px dashed silver;
}
main::before{
content: 'pseudo';
width: 100px;
height: 100px;
background: lightgreen;
position: absolute;
left: var(--before-left); /* <-- using the variable */
top: 40px;
transition:.3s ease-out;
}
<main>Hover & out</main>

How to display all CSS selectors & properties in a textarea?

I am trying to display a set of CSS properties in a textarea using JavaScript:
var exampleone = document.getElementById('th001');
var borderbox = window.getComputedStyle(exampleone).getPropertyValue('cursor');
document.getElementById("csstextareadisp").value = borderbox;
However it only displays one element, which I have to specify.
I want the JavaScript to read all properties which exist in the CSS document and display them as seen in the CSS document, e.g.
.exone{
border-style: solid;
border-width: 2px;
border-color: rgba(57,165,255,1.00);
width: 150px;
height: 30px;
position: relative;
text-align: center;
background-color: transparent;
color: black;
}
.exone:hover{
cursor: pointer;
background-color: rgba(57,165,255,1.00);
color: white;
}
My question is, is there a way I can use JavaScript to get it to display like that (seen above) in a textarea other than setting it to display using:
document.getElementById("csstextareadisp").value = ".exone{ \n border-style: solid; \n border-width: 2px; \n border-color: rgba(57,165,255,1.00); \n width: 150px; \n height: 30px; \n position: relative; \n text-align: center; \n background-color: transparent;color: black; \n } \n\n .exone:hover{ \n cursor: pointer; \n background-color: rgba(57,165,255,1.00); \n color: white; \n }";
Updated answer
There is a helpful topic here:
How to get the applied style from an element, excluding the default user agent styles
I tried to enhance the solution provided in this topic to better fit your needs by…
Adding a parameter to be able to choose whether or not to include inline style,
Adding a function to correctly indent the styles,
Trying to simplify some code.
var proto = Element.prototype;
var slice = Function.call.bind(Array.prototype.slice);
var matches = Function.call.bind(proto.matchesSelector ||
proto.mozMatchesSelector || proto.webkitMatchesSelector ||
proto.msMatchesSelector || proto.oMatchesSelector);
// Returns true if a DOM Element matches a cssRule
var elementMatchCSSRule = function(element, cssRule) {
// console.log(cssRule) //.selectorText.split(":")[0]); // Testing to add hover
return matches(element, cssRule.selectorText);
};
// Returns true if a property is defined in a cssRule
var propertyInCSSRule = function(prop, cssRule) {
return prop in cssRule.style && cssRule.style[prop] !== '';
};
// Here we get the cssRules across all the stylesheets in one array
var cssRules = slice(document.styleSheets).reduce(function(rules, styleSheet) {
return rules.concat(slice(styleSheet.cssRules));
}, []);
// Get only the css rules that matches that element
var getAppliedCSS = function(elm) {
var elementRules = cssRules.filter(elementMatchCSSRule.bind(null, elm));
var rules = [];
if (elementRules.length) {
for (i = 0; i < elementRules.length; i++) {
rules.push({
order: i,
text: elementRules[i].cssText
})
}
}
return rules;
}
// TAKIT: Added this function to indent correctly
function indentAsCSS(str) {
return str.replace(/([{;}])/g, "$1\n ").replace(/(\n[ ]+})/g, "\n}");
}
function getStyle(elm, lookInHTML = false) { // TAKIT: Added the new parameter here
var rules = getAppliedCSS(elm);
var str = '';
for (i = 0; i < rules.length; i++) {
var r = rules[i];
str += '/* CSS styling #' + r.order + ' */\n' + r.text;
}
// TAKIT: Moved and simplified the below from the other function to here
if (lookInHTML && elm.getAttribute('style')) // TAKIT: Using the new parameter
str += '\n/* Inline styling */\n' + elm.getAttribute('style');
return indentAsCSS(str);
}
// Output in textarea
var exone = document.getElementById("exone");
var result = document.getElementById("result");
result.value = getStyle(exone, true); // TAKIT: Using the new parameter for inline style
#exone {
border-style: solid;
border-width: 2px;
border-color: rgba(57, 165, 255, 1.00);
width: 150px;
height: 30px;
position: relative;
text-align: center;
background-color: transparent;
color: black;
}
#exone:hover {
cursor: pointer;
background-color: rgba(57, 165, 255, 1.00);
color: white;
}
#result {
width: 90%;
height: 240px;
}
<div id="exone" style="opacity: 0.95;"></div>
<textarea id="result"></textarea>
(I'm trying to add the :hover style to the output too, but I can't make it to work)
⋅
⋅
⋅
Old answer
(When I hadn't found anything helpful yet)
As the .getComputedStyle doesn't make any difference between the one that are present in the CSS and the other ones, it seems complicated to differenciate them.
So, here is an attempt of that:
I've made a loop to compare the element exone with another reference element that has not been stylized using CSS,
It seems that the element we take in reference must be on the page to effectively compare them, so I've put it in the HTML.
In the loop, if the values are the same, that must mean that both of them are not stylized, so, we skip to the next item.
I ended-up with that snippet:
// Get style of our example element
var exampleone = document.getElementById('exone');
var styles_one = window.getComputedStyle(exampleone);
// Get style of a reference element without CSS
var reference = document.getElementById('exref');
var styles_ref = window.getComputedStyle(reference);
// Loop and compare our example element with the reference element
var results = {};
for (var key in styles_ref) {
if(key.includes('webkit')) continue; // Next if webkit prefix
if(styles_one[key] == styles_ref[key]) continue; // Next if same value as the ref
results[key] = styles_one[key]; // Copy value in results[key]
}
delete results.cssText; // Useless in our case
// Output in console
console.log(results);
#exone {
border-style: solid;
border-width: 2px;
border-color: rgba(57, 165, 255, 1.00);
width: 150px;
height: 30px;
position: relative;
text-align: center;
background-color: transparent;
color: black;
}
#exone:hover {
cursor: pointer;
background-color: rgba(57, 165, 255, 1.00);
color: white;
}
<div id="exone"></div>
<div id="exref"></div>
The console should display only the CSS that differs from the not stylized reference element… So, this must come from the CSS!
Now, we only need to format a little this output and put it in a textarea.
Feel free to comment.
Hope it helps.

How to extend theme of draft-js-emoji-plugin

I need to extend only several CSS rules in draft-js-emoji-plugin.
Documented way is to pass theme object to configuration:
const theme = {
emojiSelectButton: 'someclassname'
};
const emojiPlugin = createEmojiPlugin({theme});
Unfortunately, this overwrites entire theme classnames instead of adding single one. Based on comments in the code this behavior is by design:
// Styles are overwritten instead of merged as merging causes a lot of confusion.
//
// Why? Because when merging a developer needs to know all of the underlying
// styles which needs a deep dive into the code. Merging also makes it prone to
// errors when upgrading as basically every styling change would become a major
// breaking change. 1px of an increased padding can break a whole layout.
In related issue developers suggested to import draft-js-emoji-plugin/lib/plugin.css and extend it in code. Anyway, each classname in this file has suffixes (CSS modules) and they might be changed so it's reliable.
I don't know how can I apply several fixes without coping entire theme.
a better method would be to import {defaultTheme} from 'draft-js-emoji-plugin' and then extend it as below:
import emojiPlugin, { defaultTheme } from 'draft-js-emoji-plugin';
// say i need to extend the emojiSelectPopover's css then.
defaultTheme.emojiSelectPopover = defaultTheme.emojiSelectPopover + " own-class"
// own class is a class with your required enhanced css. this helps in preserving the old css.
const emojiPlugin = createEmojiPlugin({
theme : defaultTheme
})
and hence use the plugin as you like.
It's nice to have such flexibility, but it really is a pain to rewrite all classes.
What I did was to extract all class names to an object, and with styled-components, interpolated the classNames to the css definition. This way you can extend whatever you want, without worrying about styling a suffixed class (and it changing when they bump a version)
In this gist I've just copied all styles in v2.1.1 of draft-js-emoji-plugin
const theme = {
emoji: 'my-emoji',
emojiSuggestions: 'my-emojiSuggestions',
emojiSuggestionsEntry: 'my-emojiSuggestionsEntry',
// ...
emojiSelect: 'emojiSelect',
emojiSelectButton: 'emojiSelectButton',
emojiSelectButtonPressed: 'emojiSelectButtonPressed',
}
const StyledEmojiSelectWrapper = styled.div`
.${theme.emojiSelect} {
display: inline-block;
}
.${theme.emojiSelectButton}, .${theme.emojiSelectButtonPressed} {
margin: 0;
padding: 0;
width: 2.5em;
height: 1.5em;
box-sizing: border-box;
line-height: 1.2em;
font-size: 1.5em;
color: #888;
background: #fff;
border: 1px solid #ddd;
border-radius: 1.5em;
cursor: pointer;
}
.${theme.emojiSelectButton}:focus, .${theme.emojiSelectButtonPressed}:focus {
outline: 0;
/* reset for :focus */
}
// ...
`
export const GlobalStyleForEmojiSelect = createGlobalStyle`
.${theme.emoji} {
background-position: center;
//...
}
export default (props) => (
<StyledEmojiSelectWrapper>
<GlobalStyleForEmojiSelect />
<EmojiSelect /> // lib button component
</StyledEmojiSelectWrapper>
)

Javascript - Function to change element colour but first call never works [duplicate]

This question already has answers here:
Javascript styling toggle doesn't work first click
(4 answers)
Closed 5 years ago.
I have a simple function written in JS that when called, will check the current background colour of the element. If it is a certain colour then it will change to a different and if it is a another colour then it will change back to the first colour - like a toggle.
The only problem I have is that the first call of the function never work but I can't work out why. Even though I am expecting it to turn blue?
function changeBgColor() {
var x = document.getElementById(dest_1);
var dest_bgColor = x.style.backgroundColor;
if (dest_bgColor === "aquamarine") {
x.style.backgroundColor = "blue";
} else {
x.style.backgroundColor = "aquamarine";
}
return;
}
.selectBox {
display: block;
width: 100px;
height: 100px;
background-color: aquamarine;
}
A
While the browser is rendering the element using the color from your style rule, the rule won't set the element's style.backgroundColor. It's a bad idea in general to rely on values in these properties, you should always keep/switch a separate variable, then only set style.x.
To find out the actual value, you can use getComputedStyle(x).backgroundColor. In your example, this returns rgb(127, 255, 212) though, not aquamarine.
var cols = ["aquamarine", "blue"];
var index = 0;
function changeBgColor() {
var x = document.getElementById("dest_1");
// move to next color
index = (index + 1) % cols.length;
x.style.backgroundColor = cols[index];
return;
}
.selectBox {
display: block;
width: 100px;
height: 100px;
background-color: aquamarine;
}
A

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";
}

Categories

Resources