Selecting and manipulating CSS pseudo-elements such as ::before and ::after using javascript (or jQuery)
Asked Answered
D

28

1144

Is there any way to select/manipulate CSS pseudo-elements such as ::before and ::after (and the old version with one semi-colon) using jQuery?

For example, my stylesheet has the following rule:

.span::after{ content:'foo' }

How can I change 'foo' to 'bar' using vanilla JS or jQuery?

Dodd answered 18/2, 2011 at 12:53 Comment(1)
See github.com/guest271314/pseudo/blob/master/pseudo.jsLengthwise
C
779

You could also pass the content to the pseudo element with a data attribute and then use jQuery to manipulate that:

In HTML:

<span>foo</span>

In jQuery:

$('span').hover(function(){
    $(this).attr('data-content','bar');
});

In CSS:

span:after {
    content: attr(data-content) ' any other text you may want';
}

If you want to prevent the 'other text' from showing up, you could combine this with seucolega's solution like this:

In HTML:

<span>foo</span>

In jQuery:

$('span').hover(function(){
    $(this).addClass('change').attr('data-content','bar');
});

In CSS:

span.change:after {
    content: attr(data-content) ' any other text you may want';
}
Curst answered 20/4, 2011 at 17:59 Comment(12)
Do you have a link in the spec for using that attr function against the content property? I'm surprised I've never heard of this...Outlawry
+1 for attr(), too bad i wasn't able to use it with other properties than content. DemoAmerson
That's because no browser in existence has implemented attr() beyond CSS2 yet, whereas in CSS2 itself attr() has only been defined for the content property.Bonar
This does not work for image urls like background-image: url( attr(data-image, url) );Unwitting
@chovy: See my comment for the reason why.Bonar
How could I use this to pass an img url ? : .attr("data-content",url('../images/loader.gif'));Bellflower
Updated link for Attribute References: w3.org/TR/css3-values/#attr-notationHalation
hmm this won't work if you want to change other properties of the pseudo element such as margin via a jQuery animation.Tuckie
@chofy @Sumit That's true; you can currently only use attr() in CSS to read simple strings for the content property.Bearberry
Does anyone know the browser compatibility for this? I assume this won't work for certain versions of IE.Polaroid
@Johannes If a browser supports :before, it supports attr() in CSSBearberry
Still no support after 5 years.Suppositious
B
561

You'd think this would be a simple question to answer, with everything else that jQuery can do. Unfortunately, the problem comes down to a technical issue: css :after and :before rules aren't part of the DOM, and therefore can't be altered using jQuery's DOM methods.

There are ways to manipulate these elements using JavaScript and/or CSS workarounds; which one you use depends on your exact requirements.


I'm going to start with what's widely considered the "best" approach:

1) Add/remove a predetermined class

In this approach, you've already created a class in your CSS with a different :after or :before style. Place this "new" class later in your stylesheet to make sure it overrides:

p:before {
    content: "foo";
}
p.special:before {
    content: "bar";
}

Then you can easily add or remove this class using jQuery (or vanilla JavaScript):

$('p').on('click', function() {
    $(this).toggleClass('special');
});

    $('p').on('click', function() {
      $(this).toggleClass('special');
    });
p:before {
  content: "foo";
  color: red;
  cursor: pointer;
}
p.special:before {
  content: "bar";
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<p>This is a paragraph.</p>
<p>This is another paragraph.</p>
  • Pros: Easy to implement with jQuery; quickly alters multiple styles at once; enforces separation of concerns (isolating your CSS and JS from your HTML)
  • Cons: CSS must be pre-written, so the content of :before or :after isn't completely dynamic

2) Add new styles directly to the document's stylesheet

It's possible to use JavaScript to add styles directly to the document stylesheet, including :after and :before styles. jQuery doesn't provide a convenient shortcut, but fortunately the JS isn't that complicated:

var str = "bar";
document.styleSheets[0].addRule('p.special:before','content: "'+str+'";');

var str = "bar";
document.styleSheets[0].addRule('p.special:before', 'content: "' + str + '";');
p:before {
  content: "foo";
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<p class="special">This is a paragraph</p>
<p>This is another paragraph</p>

.addRule() and the related .insertRule() methods are fairly well-supported today.

As a variation, you can also use jQuery to add an entirely new stylesheet to the document, but the necessary code isn't any cleaner:

var str = "bar";
$('<style>p.special:before{content:"'+str+'"}</style>').appendTo('head');

var str = "bar";
$('<style>p.special:before{content:"' + str + '"}</style>').appendTo('head');
p:before {
  content: "foo";
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<p class="special">This is a paragraph</p>
<p>This is another paragraph</p>

If we're talking about "manipulating" the values, not just adding to them, we can also read the existing :after or :before styles using a different approach:

var str = window.getComputedStyle(document.querySelector('p'), ':before') 
           .getPropertyValue('content');

var str = window.getComputedStyle($('p')[0], ':before').getPropertyValue('content');
console.log(str);

document.styleSheets[0].addRule('p.special:before', 'content: "' + str+str + '";');
p:before {
    content:"foo";
    color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<p class="special">This is a paragraph</p>
<p>This is another paragraph</p>

We can replace document.querySelector('p') with $('p')[0] when using jQuery, for slightly shorter code.

  • Pros: any string can be dynamically inserted into the style
  • Cons: original styles aren't altered, just overridden; repeated (ab)use can make the DOM grow arbitrarily large

3) Alter a different DOM attribute

You can also to use attr() in your CSS to read a particular DOM attribute. (If a browser supports :before, it supports attr() as well.) By combining this with content: in some carefully-prepared CSS, we can change the content (but not other properties, like margin or color) of :before and :after dynamically:

p:before {
    content: attr(data-before);
    color: red;
    cursor: pointer;
}

JS:

$('p').on('click', function () {
    $(this).attr('data-before','bar');
});

$('p').on('click', function () {
    $(this).attr('data-before','bar');
});
p:before {
    content: attr(data-before);
    color: red;
    cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<p>This is a paragraph.</p>
<p>This is another paragraph.</p>

This can be combined with the second technique if the CSS can't be prepared ahead of time:

var str = "bar";

document.styleSheets[0].addRule('p:before', 'content: attr(data-before);');

$('p').on('click', function () {
    $(this).attr('data-before', str);
});

var str = "bar";
document.styleSheets[0].addRule('p:before', 'content: attr(data-before) !important;');

$('p').on('click', function() {
  $(this).attr('data-before', str);
});
p:before {
  content: "foo";
  color: red;
  cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<p>This is a paragraph.</p>
<p>This is another paragraph.</p>
  • Pros: Doesn't create endless extra styles
  • Cons: attr in CSS can only apply to content strings, not URLs or RGB colors
Bearberry answered 11/2, 2014 at 18:14 Comment(9)
I'm trying to dynamically set glyphicon values (i.e., via their hex values) in ::after psedo. content: element (e.g., content: "\e043";). it doesn't seem to work for me so I'm assuming it doesn't work for hex values for glyphicons either?Isabel
@Isabel You should post that as a new question. I'd have to see all the code you're using.Bearberry
Blazemonger, thanks for the quick reply..unfortunately there is a quite a bit of code and it would take quite a bit of effort to snip out the relevant code. I've already spent 12+ hours trying to get this work and this was my last gasp effort to get it to work. I need to cut my losses. I was hoping you might be able to just verify my assumption re: hex values when using the technique you described in #3 above (before code snippet). I can insert hex string in content element but it displays text for glyphicon hex value rather than the actual glyphicon. Impression without seeing all code?Isabel
@Isabel Don't use the hex string; instead, copy and paste the actual Unicode character into the HTML attribute. jsfiddle.net/mblase75/Lcsjkc5yBearberry
regarding solution 2. & 3. actually you can prevent stylesheet from (over)growing if you use: document.styleSheets[0].insertRule(rule, index), then using this index you can remove the rules when not needed: document.styleSheets[0].deleteRule(index)Midpoint
It is also important to note that option #2 here will not work if your site has CSP rules for the style-src that don't have 'unsafe-inline'. The style will not be added and you will see a CSP error in the console.Chaetopod
how can i get an image which is inside ::beforeRepulsive
Down-voted for representing jQuery as JavaScript.Intermeddle
addRule() is deprecated. This answer should be updated to emphasize the use of insertRule().Reynolds
B
170

Although they are rendered by browsers through CSS as if they were like other real DOM elements, pseudo-elements themselves are not part of the DOM, because pseudo-elements, as the name implies, are not real elements, and therefore you can't select and manipulate them directly with jQuery (or any JavaScript APIs for that matter, not even the Selectors API). This applies to any pseudo-elements whose styles you're trying to modify with a script, and not just ::before and ::after.

You can only access pseudo-element styles directly at runtime via the CSSOM (think window.getComputedStyle()), which is not exposed by jQuery beyond .css(), a method that doesn't support pseudo-elements either.

You can always find other ways around it, though, for example:

  • Applying the styles to the pseudo-elements of one or more arbitrary classes, then toggling between classes (see seucolega's answer for a quick example) — this is the idiomatic way as it makes use of simple selectors (which pseudo-elements are not) to distinguish between elements and element states, the way they're intended to be used

  • Manipulating the styles being applied to said pseudo-elements, by altering the document stylesheet, which is much more of a hack

Bonar answered 18/2, 2011 at 12:56 Comment(0)
U
98

We can also rely on custom properties (aka CSS variables) in order to manipulate pseudo-element. We can read in the specification that:

Custom properties are ordinary properties, so they can be declared on any element, are resolved with the normal inheritance and cascade rules, can be made conditional with @media and other conditional rules, can be used in HTML’s style attribute, can be read or set using the CSSOM, etc.

Considering this, the idea is to define the custom property within the element and the pseudo-element will simply inherit it; thus we can easily modify it.

1) Using inline style:

.box:before {
  content:var(--content,"I am a before element");
  color:var(--color, red);
  font-size:25px;
}
<div class="box"></div>
<div class="box" style="--color:blue;--content:'I am a blue element'"></div>
<div class="box" style="--color:black"></div>
<div class="box" style="--color:#f0f;--content:'another element'"></div>

2) Using CSS and classes

.box:before {
  content:var(--content,"I am a before element");
  color:var(--color, red);
  font-size:25px;
}

.blue {
  --color:blue;
  --content:'I am a blue element';
}
.black {
  --color:black;
}
<div class="box"></div>
<div class="box black" ></div>
<div class="box blue"></div>

3) Using javascript

document.querySelectorAll('.box')[0].style.setProperty("--color", "blue");
document.querySelectorAll('.box')[1].style.setProperty("--content", "'I am another element'");
.box:before {
  content:var(--content,"I am a before element");
  color:var(--color, red);
  font-size:25px;
}
<div class="box"></div>
<div class="box"></div>

4) Using jQuery

$('.box').eq(0).css("--color", "blue");
/* the css() function with custom properties works only with a jQuery vesion >= 3.x
   with older version we can use style attribute to set the value. Simply pay
   attention if you already have inline style defined! 
*/
$('.box').eq(1).attr("style","--color:#f0f");
.box:before {
  content:"I am a before element";
  color:var(--color, red);
  font-size:25px;
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>

It can also be used with complex values:

.box {
  --c:"content";
  --b:linear-gradient(red,blue);
  --s:20px;
  --p:0 15px;
}

.box:before {
  content: var(--c);
  background:var(--b);
  color:#fff;
  font-size: calc(2 * var(--s) + 5px);
  padding:var(--p);
}
<div class="box"></div>

You may notice that I am considering the syntax var(--c,value) where value is the default value and also called the fallback value.

From the same specification we can read:

The value of a custom property can be substituted into the value of another property with the var() function. The syntax of var() is:

var() = var( <custom-property-name> [, <declaration-value> ]? )

The first argument to the function is the name of the custom property to be substituted. The second argument to the function, if provided, is a fallback value, which is used as the substitution value when the referenced custom property is invalid.

And later:

To substitute a var() in a property’s value:

  1. If the custom property named by the first argument to the var() function is animation-tainted, and the var() function is being used in the animation property or one of its longhands, treat the custom property as having its initial value for the rest of this algorithm.
  2. If the value of the custom property named by the first argument to the var() function is anything but the initial value, replace the var() function by the value of the corresponding custom property.
  3. Otherwise, if the var() function has a fallback value as its second argument, replace the var() function by the fallback value. If there are any var() references in the fallback, substitute them as well.
  4. Otherwise, the property containing the var() function is invalid at computed-value time.

If we don't set the custom property OR we set it to initial OR it contains an invalid value then the fallback value will be used. The use of initial can be helpful in case we want to reset a custom property to its default value.

Related

How to store inherit value inside a CSS variable (aka custom property)?

CSS custom properties (variables) for box model

Unspeakable answered 2/4, 2018 at 21:15 Comment(7)
@akalata yes, the code require a jQuery version 3.x .. I added more details and another alternative with jQuery ;)Unspeakable
How would this perform in IE 11?Stalk
@Stalk like any good and modern feature .. it's not supported and will not work caniuse.com/#feat=css-variablesUnspeakable
Ofc I knew that and since IE 11 is still very relevant, I missed that piece of information right in the introductory part of your answer.Stalk
@Stalk you will never find such thing in my answers :) IE doesn't exist in my world ... but feel free to edit and add any information you think relevant.Unspeakable
Added that note in a (not overly) prominent position.Stalk
This answer is an in depth tutorial of relating and modifying pseudo-elements with JavaScript using CSS Variables. Thank you very much for your time and for sharing this extremely valuable technique(s).Photothermic
I
93

You can't select pseudo elements in jQuery because they are not part of DOM. But you can add a specific class to the parent element and control its pseudo elements in CSS.

EXAMPLE

In jQuery:

<script type="text/javascript">
    $('span').addClass('change');
</script>

In CSS:

span.change:after { content: 'bar' }
Intonation answered 17/3, 2011 at 6:46 Comment(0)
B
42

In the line of what Christian suggests, you could also do:

$('head').append("<style>.span::after{ content:'bar' }</style>");
Bromo answered 18/10, 2012 at 10:20 Comment(2)
Here should be added an id-attribute, so the element can be selected and deleted before a new one is added. If not, there can come up a lot of unnecessary style-nodes.Bifurcate
Your sample unfortunately did not work, but this did it. const setPseudoElContent = (selector, value) => { const style = document.createElement('style'); style.innerHTML = ${selector} {${value}; }; document.head.appendChild(style); }Svoboda
H
33

Here is the way to access :after and :before style properties, defined in css:

// Get the color value of .element:before
var color = window.getComputedStyle(
    document.querySelector('.element'), ':before'
).getPropertyValue('color');

// Get the content value of .element:before
var content = window.getComputedStyle(
    document.querySelector('.element'), ':before'
).getPropertyValue('content');
Hardin answered 15/7, 2014 at 21:55 Comment(1)
this is how to access but how to change ?Demesne
M
14

IF you want to to manipulate the ::before or ::after sudo elements entirely through CSS, you could do it JS. See below;

jQuery('head').append('<style id="mystyle" type="text/css"> /* your styles here */ </style>');

Notice how the <style> element has an ID, which you can use to remove it and append to it again if your style changes dynamically.

This way, your element is style exactly how you want it through CSS, with the help of JS.

Maritsa answered 14/6, 2014 at 20:13 Comment(0)
T
9

Thank you all! i managed to do what i wanted :D http://jsfiddle.net/Tfc9j/42/ here take a look

i wanted to have the opacity of an outer div to be different from the opacity of the internal div and that change with a click somwewhere ;) Thanks!

   $('#ena').on('click', function () {
        $('head').append("<style>#ena:before { opacity:0.3; }</style>");
    });

$('#duop').on('click', function (e) {

        $('head').append("<style>#ena:before { opacity:0.8; }</style>");

     e.stopPropagation(); 
    });

#ena{
    width:300px;
    height:300px;
    border:1px black solid;
    position:relative;
}
#duo{
    opacity:1;
    position:absolute;
    top:50px;
  width:300px;
    height:100px;
      background-color:white;
}
#ena:before {
    content: attr(data-before);
    color: white;
    cursor: pointer;
    position: absolute;
    background-color:red;
    opacity:0.9;
    width:100%;
    height:100%;
}


<div id="ena">
    <div id="duo">
        <p>ena p</p>
        <p id="duop">duoyyyyyyyyyyyyyy p</p>

    </div>   


</div>
Tenpins answered 10/7, 2014 at 15:26 Comment(2)
Not try append, use html is best preventHamilton
Watch out: here you are appending a style tag to the head every time one of those clicks is handled. I'd code a way to remove the old one before adding a new one.Chromatic
M
7

one working but not very efficient way is to add a rule to the document with the new content and reference it with a class. depending on what is needed the class might need an unique id for each value in content.

$("<style type='text/css'>span.id-after:after{content:bar;}</style>").appendTo($("head"));
$('span').addClass('id-after');
Mombasa answered 12/9, 2012 at 13:27 Comment(0)
A
5

Here is the HTML:

<div class="icon">
  <span class="play">
    ::before
  </span>
</div>

Computed style on 'before' was content: "VERIFY TO WATCH";

Here is my two lines of jQuery, which use the idea of adding an extra class to specifically reference this element and then appending a style tag (with an !important tag) to changes the CSS of the sudo-element's content value:

$("span.play:eq(0)").addClass('G');

$('body').append("<style>.G:before{content:'NewText' !important}</style>");

Approve answered 18/3, 2014 at 23:27 Comment(0)
S
5

I'm always adding my own utils function, which looks like this.

function setPseudoElContent(selector, value) {    
    document.styleSheets[0].addRule(selector, 'content: "' + value + '";');
}

setPseudoElContent('.class::after', 'Hello World!');

or make use of ES6 Features:

const setPseudoElContent = (selector, value) => {    
    document.styleSheets[0].addRule(selector, `content: "${value}";`);
}

setPseudoElContent('.class::after', 'Hello World!');
Sike answered 13/9, 2018 at 9:56 Comment(0)
C
4

You may create a fake property or use an existing one and inherit it in the pseudo-element's stylesheet.

var switched = false;

// Enable color switching
setInterval(function () {
    var color = switched ? 'red' : 'darkred';
    var element = document.getElementById('arrow');
    element.style.backgroundColor = color;
    
    // Managing pseudo-element's css
    // using inheritance.
    element.style.borderLeftColor = color;
    
    switched = !switched;
}, 1000);
.arrow {
    /* SET FICTIONAL PROPERTY */
    border-left-color:red;
    
    background-color:red;
    width:1em;
    height:1em;
    display:inline-block;
    position:relative;
}
.arrow:after {
    border-top:1em solid transparent;
    border-right:1em solid transparent;
    border-bottom:1em solid transparent;
    border-left:1em solid transparent;
    
    /* INHERIT PROPERTY */
    border-left-color:inherit;
    
    content:"";
    width:0;
    height:0;
    position:absolute;
    left:100%;
    top:-50%;
}
<span id="arrow" class="arrow"></span>

It seems it doesn't work for "content" property :(

Conceptionconceptual answered 13/10, 2014 at 13:45 Comment(0)
O
4

This is not practical as i did not write this for real world uses, just to give you a example of what can be achieved.

css = {
before: function(elem,attr){ 

if($("#cust_style") !== undefined){ 
$("body").append("<style> " + elem + ":before {"  + attr +  "} </style>"); 
} else {
 $("#cust_style").remove();
$("body").append("<style> " + elem + ":before {"  + attr +  "} </style>"); 
}

}, after: function(elem,attr){
if($("#cust_style") !== undefined){ 
$("body").append("<style> " + elem + ":after {"  + attr +  "} </style>"); 

} else { $("#cust_style").remove();
$("body").append("<style> " + elem + ":after {"  + attr +  "} </style>"); 
}
}
}

this currently add's a / or appends a Style element which contains your necessary attribute's which will take affect on the target element's after Pseudo element.

this can be used as

css.after("someElement"," content: 'Test'; position: 'absolute'; ") // editing / adding styles to :after

and

css.before( ... ); // to affect the before pseudo element.

as after: and before: pseudo elements are not directly accessible through DOM it is currently not possible to edit the Specific values of the css freely.

my way was just a example and its not good for practice, you can modify it try some of your own tricks and make it correct for real world usage.

so do your own experimentation's with this and others!

regards - Adarsh Hegde.

Outspread answered 20/4, 2015 at 11:59 Comment(0)
W
4

Why adding classes or attributes when you can just append a style to head

$('head').append('<style>.span:after{ content:'changed content' }</style>')
Woofer answered 9/8, 2016 at 6:10 Comment(0)
R
3

There are many answers here but no answer helps to manipulate the css of :before or :after , not even the accepted one.

Here is how I propose to do it. Lets suppose your HTML is like this:

<div id="something">Test</div>

And then you are setting its :before in CSS and designing it like:

#something:before{
   content:"1st";
   font-size:20px;
   color:red;
}
#something{
  content:'1st';
}

Please notice I also set content attribute in element itself so that you can take it out easily later. Now there is a button clicking on which, you want to change the color of :before to green and its font-size to 30px. You can achieve that as follows:

Define a css with your required style on some class .activeS :

.activeS:before{
   color:green !important;
   font-size:30px !important;
 }

Now you can change :before style by adding the class to your :before element as follows:

<button id="changeBefore">Change</button>
<script>
    $('#changeBefore').click(function(){
        $('#something').addClass('activeS');
    });
</script>

If you just want to get content of :before, it can be done as:

<button id="getContent">Get Content</button>
<script>
    $('#getContent').click(function(){
        console.log($('#something').css('content'));//will print '1st'
    });
</script>

Ultimately if you want to dynamically change :before content by jQuery, You can achieve that as follows:

<button id="changeBefore">Change</button>
<script>
    var newValue = '22';//coming from somewhere
    var add = '<style>#something:before{content:"'+newValue+'"!important;}</style>';
    $('#changeBefore').click(function(){
        $('body').append(add);
    });
</script>

Clicking on above "changeBefore" button will change :before content of #something into '22' which is a dynamic value.

I hope it helps

Ruthenian answered 14/11, 2016 at 13:25 Comment(0)
A
2

I have created a jQuery plugin to add css-pseudo rules like using .css() for specific elements.

  • plugin code and test case is here (updated Fiddle)
  • use case as simple css image popup here

usage:

$('body')
  .css({
    backgroundColor: 'white'
  })
  .cssPseudo('after', {
    content: 'attr(title) ", you should try to hover the picture, then click it."',
    position: 'absolute',
    top: 20, left: 20  
  })
  .cssPseudo('hover:after', {
    content: '"Now hover the picture, then click it!"'
  });

Accumbent answered 3/2, 2019 at 6:55 Comment(0)
S
2

The below solution tells how you can update pseudo-elements with javascript attr attribute.

add an attribute in HTML which you can manipulate with javascript with setAttribute.

<div 
 id="inputBoxParent" 
 count="0">
      ...
</div>

update with js

inputBoxParent.setAttribute('count', value.length)

CSS - in pseudo-element add content as attr(attributeName)

.input-box-container::after{
  content: attr(count);
}

And you are done!!!

const inputBoxParent = document.getElementById("inputBoxParent");
const handleOnChange = (value) => {
  inputBoxParent.setAttribute('count', value.length)
}
.input-box-container {
  position: relative;
  width: 200px;
}
.input-box-container::after{
  position: absolute;
  bottom: 8px;
  right: 10px;
  height: 10px;
  width: 20px;
  content: attr(count);
}
<h4> Type some text inside the box and click outside to see resule i.e. pseudo element content change</h4>
<div 
  id="inputBoxParent" 
  class="input-box-container" 
  count="0">
  <input 
    type="text" 
    id="inputBox" 
    placeholder="type some thing" 
    onchange="handleOnChange(this.value)"
    onkeyup="handleOnChange(this.value)"
 />
</div>
Snashall answered 20/3, 2021 at 10:10 Comment(1)
What if there is no content attribute to begin with? That is, how to construct the entire CSS using only Javascript? It seems if i read content it's an empty string, but this is a lie because there is no ::before selector attached to my element. And if I try to write (assign) any other value, when I read it back, it returns the empty string!Yggdrasil
R
2

In Vanilla JS you can do something like this: 1- instead of editing ::before ::after directly, use css variables!

in css:

:root{
   --bg: red;
}

Our element:

div::after{
   background: var(--bg) 
}

In JS change it like this:

let r = document.querySelector(':root');
r.style.setProperty('--bg', `blue`)
Reject answered 25/5, 2023 at 10:50 Comment(0)
H
1

You can use my plugin for this purpose.

JQuery:

(function() {
  $.pseudoElements = {
    length: 0
  };

  var setPseudoElement = function(parameters) {
    if (typeof parameters.argument === 'object' || (parameters.argument !== undefined && parameters.property !== undefined)) {
      for (var element of parameters.elements.get()) {
        if (!element.pseudoElements) element.pseudoElements = {
          styleSheet: null,
          before: {
            index: null,
            properties: null
          },
          after: {
            index: null,
            properties: null
          },
          id: null
        };

        var selector = (function() {
          if (element.pseudoElements.id !== null) {
            if (Number(element.getAttribute('data-pe--id')) !== element.pseudoElements.id) element.setAttribute('data-pe--id', element.pseudoElements.id);
            return '[data-pe--id="' + element.pseudoElements.id + '"]::' + parameters.pseudoElement;
          } else {
            var id = $.pseudoElements.length;
            $.pseudoElements.length++

              element.pseudoElements.id = id;
            element.setAttribute('data-pe--id', id);

            return '[data-pe--id="' + id + '"]::' + parameters.pseudoElement;
          };
        })();

        if (!element.pseudoElements.styleSheet) {
          if (document.styleSheets[0]) {
            element.pseudoElements.styleSheet = document.styleSheets[0];
          } else {
            var styleSheet = document.createElement('style');

            document.head.appendChild(styleSheet);
            element.pseudoElements.styleSheet = styleSheet.sheet;
          };
        };

        if (element.pseudoElements[parameters.pseudoElement].properties && element.pseudoElements[parameters.pseudoElement].index) {
          element.pseudoElements.styleSheet.deleteRule(element.pseudoElements[parameters.pseudoElement].index);
        };

        if (typeof parameters.argument === 'object') {
          parameters.argument = $.extend({}, parameters.argument);

          if (!element.pseudoElements[parameters.pseudoElement].properties && !element.pseudoElements[parameters.pseudoElement].index) {
            var newIndex = element.pseudoElements.styleSheet.rules.length || element.pseudoElements.styleSheet.cssRules.length || element.pseudoElements.styleSheet.length;

            element.pseudoElements[parameters.pseudoElement].index = newIndex;
            element.pseudoElements[parameters.pseudoElement].properties = parameters.argument;
          };

          var properties = '';

          for (var property in parameters.argument) {
            if (typeof parameters.argument[property] === 'function')
              element.pseudoElements[parameters.pseudoElement].properties[property] = parameters.argument[property]();
            else
              element.pseudoElements[parameters.pseudoElement].properties[property] = parameters.argument[property];
          };

          for (var property in element.pseudoElements[parameters.pseudoElement].properties) {
            properties += property + ': ' + element.pseudoElements[parameters.pseudoElement].properties[property] + ' !important; ';
          };

          element.pseudoElements.styleSheet.addRule(selector, properties, element.pseudoElements[parameters.pseudoElement].index);
        } else if (parameters.argument !== undefined && parameters.property !== undefined) {
          if (!element.pseudoElements[parameters.pseudoElement].properties && !element.pseudoElements[parameters.pseudoElement].index) {
            var newIndex = element.pseudoElements.styleSheet.rules.length || element.pseudoElements.styleSheet.cssRules.length || element.pseudoElements.styleSheet.length;

            element.pseudoElements[parameters.pseudoElement].index = newIndex;
            element.pseudoElements[parameters.pseudoElement].properties = {};
          };

          if (typeof parameters.property === 'function')
            element.pseudoElements[parameters.pseudoElement].properties[parameters.argument] = parameters.property();
          else
            element.pseudoElements[parameters.pseudoElement].properties[parameters.argument] = parameters.property;

          var properties = '';

          for (var property in element.pseudoElements[parameters.pseudoElement].properties) {
            properties += property + ': ' + element.pseudoElements[parameters.pseudoElement].properties[property] + ' !important; ';
          };

          element.pseudoElements.styleSheet.addRule(selector, properties, element.pseudoElements[parameters.pseudoElement].index);
        };
      };

      return $(parameters.elements);
    } else if (parameters.argument !== undefined && parameters.property === undefined) {
      var element = $(parameters.elements).get(0);

      var windowStyle = window.getComputedStyle(
        element, '::' + parameters.pseudoElement
      ).getPropertyValue(parameters.argument);

      if (element.pseudoElements) {
        return $(parameters.elements).get(0).pseudoElements[parameters.pseudoElement].properties[parameters.argument] || windowStyle;
      } else {
        return windowStyle || null;
      };
    } else {
      console.error('Invalid values!');
      return false;
    };
  };

  $.fn.cssBefore = function(argument, property) {
    return setPseudoElement({
      elements: this,
      pseudoElement: 'before',
      argument: argument,
      property: property
    });
  };
  $.fn.cssAfter = function(argument, property) {
    return setPseudoElement({
      elements: this,
      pseudoElement: 'after',
      argument: argument,
      property: property
    });
  };
})();

$(function() {
  $('.element').cssBefore('content', '"New before!"');
});
.element {
  width: 480px;
  margin: 0 auto;
  border: 2px solid red;
}

.element::before {
  content: 'Old before!';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

<div class="element"></div>

The values should be specified, as in the normal function of jQuery.css

In addition, you can also get the value of the pseudo-element parameter, as in the normal function of jQuery.css:

console.log( $(element).cssBefore(parameter) );

JS:

(function() {
  document.pseudoElements = {
    length: 0
  };

  var setPseudoElement = function(parameters) {
    if (typeof parameters.argument === 'object' || (parameters.argument !== undefined && parameters.property !== undefined)) {
      if (!parameters.element.pseudoElements) parameters.element.pseudoElements = {
        styleSheet: null,
        before: {
          index: null,
          properties: null
        },
        after: {
          index: null,
          properties: null
        },
        id: null
      };

      var selector = (function() {
        if (parameters.element.pseudoElements.id !== null) {
          if (Number(parameters.element.getAttribute('data-pe--id')) !== parameters.element.pseudoElements.id) parameters.element.setAttribute('data-pe--id', parameters.element.pseudoElements.id);
          return '[data-pe--id="' + parameters.element.pseudoElements.id + '"]::' + parameters.pseudoElement;
        } else {
          var id = document.pseudoElements.length;
          document.pseudoElements.length++

            parameters.element.pseudoElements.id = id;
          parameters.element.setAttribute('data-pe--id', id);

          return '[data-pe--id="' + id + '"]::' + parameters.pseudoElement;
        };
      })();

      if (!parameters.element.pseudoElements.styleSheet) {
        if (document.styleSheets[0]) {
          parameters.element.pseudoElements.styleSheet = document.styleSheets[0];
        } else {
          var styleSheet = document.createElement('style');

          document.head.appendChild(styleSheet);
          parameters.element.pseudoElements.styleSheet = styleSheet.sheet;
        };
      };

      if (parameters.element.pseudoElements[parameters.pseudoElement].properties && parameters.element.pseudoElements[parameters.pseudoElement].index) {
        parameters.element.pseudoElements.styleSheet.deleteRule(parameters.element.pseudoElements[parameters.pseudoElement].index);
      };

      if (typeof parameters.argument === 'object') {
        parameters.argument = (function() {
          var cloneObject = typeof parameters.argument.pop === 'function' ? [] : {};

          for (var property in parameters.argument) {
            cloneObject[property] = parameters.argument[property];
          };

          return cloneObject;
        })();

        if (!parameters.element.pseudoElements[parameters.pseudoElement].properties && !parameters.element.pseudoElements[parameters.pseudoElement].index) {
          var newIndex = parameters.element.pseudoElements.styleSheet.rules.length || parameters.element.pseudoElements.styleSheet.cssRules.length || parameters.element.pseudoElements.styleSheet.length;

          parameters.element.pseudoElements[parameters.pseudoElement].index = newIndex;
          parameters.element.pseudoElements[parameters.pseudoElement].properties = parameters.argument;
        };

        var properties = '';

        for (var property in parameters.argument) {
          if (typeof parameters.argument[property] === 'function')
            parameters.element.pseudoElements[parameters.pseudoElement].properties[property] = parameters.argument[property]();
          else
            parameters.element.pseudoElements[parameters.pseudoElement].properties[property] = parameters.argument[property];
        };

        for (var property in parameters.element.pseudoElements[parameters.pseudoElement].properties) {
          properties += property + ': ' + parameters.element.pseudoElements[parameters.pseudoElement].properties[property] + ' !important; ';
        };

        parameters.element.pseudoElements.styleSheet.addRule(selector, properties, parameters.element.pseudoElements[parameters.pseudoElement].index);
      } else if (parameters.argument !== undefined && parameters.property !== undefined) {
        if (!parameters.element.pseudoElements[parameters.pseudoElement].properties && !parameters.element.pseudoElements[parameters.pseudoElement].index) {
          var newIndex = parameters.element.pseudoElements.styleSheet.rules.length || parameters.element.pseudoElements.styleSheet.cssRules.length || parameters.element.pseudoElements.styleSheet.length;

          parameters.element.pseudoElements[parameters.pseudoElement].index = newIndex;
          parameters.element.pseudoElements[parameters.pseudoElement].properties = {};
        };

        if (typeof parameters.property === 'function')
          parameters.element.pseudoElements[parameters.pseudoElement].properties[parameters.argument] = parameters.property();
        else
          parameters.element.pseudoElements[parameters.pseudoElement].properties[parameters.argument] = parameters.property;

        var properties = '';

        for (var property in parameters.element.pseudoElements[parameters.pseudoElement].properties) {
          properties += property + ': ' + parameters.element.pseudoElements[parameters.pseudoElement].properties[property] + ' !important; ';
        };

        parameters.element.pseudoElements.styleSheet.addRule(selector, properties, parameters.element.pseudoElements[parameters.pseudoElement].index);
      };
    } else if (parameters.argument !== undefined && parameters.property === undefined) {
      var windowStyle = window.getComputedStyle(
        parameters.element, '::' + parameters.pseudoElement
      ).getPropertyValue(parameters.argument);

      if (parameters.element.pseudoElements) {
        return parameters.element.pseudoElements[parameters.pseudoElement].properties[parameters.argument] || windowStyle;
      } else {
        return windowStyle || null;
      };
    } else {
      console.error('Invalid values!');
      return false;
    };
  };

  Object.defineProperty(Element.prototype, 'styleBefore', {
    enumerable: false,
    value: function(argument, property) {
      return setPseudoElement({
        element: this,
        pseudoElement: 'before',
        argument: argument,
        property: property
      });
    }
  });
  Object.defineProperty(Element.prototype, 'styleAfter', {
    enumerable: false,
    value: function(argument, property) {
      return setPseudoElement({
        element: this,
        pseudoElement: 'after',
        argument: argument,
        property: property
      });
    }
  });
})();

document.querySelector('.element').styleBefore('content', '"New before!"');
.element {
  width: 480px;
  margin: 0 auto;
  border: 2px solid red;
}

.element::before {
  content: 'Old before!';
}
<div class="element"></div>

GitHub: https://github.com/yuri-spivak/managing-the-properties-of-pseudo-elements/

Hallux answered 31/7, 2017 at 18:19 Comment(0)
M
1

Someone else commented on appending to the head element with a full style element and that's not bad if you're only doing it once but if you need to reset it more than once you'll end up with a ton of style elements. So to prevent that I created a blank style element in the head with an id and replace the innerHTML of it like this:

<style id="pseudo"></style>

Then the JavaScript would look like this:

var pseudo = document.getElementById("pseudo");

function setHeight() {
    let height = document.getElementById("container").clientHeight;
    pseudo.innerHTML = `.class:before { height: ${height}px; }`
}

setHeight()

Now in my case I needed this to set the height of a before element based on the height of another and it will change on resize so using this I can run setHeight() every time the window is resized and it will replace the <style> properly.

Hope that helps someone who was stuck trying to do the same thing.

Moonlit answered 22/9, 2017 at 8:31 Comment(0)
A
1

I made use of variables defined in :root inside CSS to modify the :after (the same applies to :before) pseudo-element, in particular to change the background-color value for a styled anchor defined by .sliding-middle-out:hover:after and the content value for another anchor (#reference) in the following demo that generates random colors by using JavaScript/jQuery:

HTML

<a href="#" id="changeColor" class="sliding-middle-out" title="Generate a random color">Change link color</a>
<span id="log"></span>
<h6>
  <a href="https://mcmap.net/q/45608/-selecting-and-manipulating-css-pseudo-elements-such-as-before-and-after-using-javascript-or-jquery" id="reference" class="sliding-middle-out" target="_blank" title="Stack Overflow topic">Reference</a>
</h6>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/davidmerfield/randomColor/master/randomColor.js"></script>

CSS

:root {
    --anchorsFg: #0DAFA4;
}
a, a:visited, a:focus, a:active {
    text-decoration: none;
    color: var(--anchorsFg);
    outline: 0;
    font-style: italic;

    -webkit-transition: color 250ms ease-in-out;
    -moz-transition: color 250ms ease-in-out;
    -ms-transition: color 250ms ease-in-out;
    -o-transition: color 250ms ease-in-out;
    transition: color 250ms ease-in-out;
}
.sliding-middle-out {
    display: inline-block;
    position: relative;
    padding-bottom: 1px;
}
.sliding-middle-out:after {
    content: '';
    display: block;
    margin: auto;
    height: 1px;
    width: 0px;
    background-color: transparent;

    -webkit-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
    -moz-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
    -ms-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
    -o-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
    transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
}
.sliding-middle-out:hover:after {
    width: 100%;
    background-color: var(--anchorsFg);
    outline: 0;
}
#reference {
  margin-top: 20px;
}
.sliding-middle-out:before {
  content: attr(data-content);
  display: attr(data-display);
}

JS/jQuery

var anchorsFg = randomColor();
$( ".sliding-middle-out" ).hover(function(){
    $( ":root" ).css({"--anchorsFg" : anchorsFg});
});

$( "#reference" ).hover(
 function(){
    $(this).attr("data-content", "Hello World!").attr("data-display", "block").html("");
 },
 function(){
    $(this).attr("data-content", "Reference").attr("data-display", "inline").html("");
 }
);
Amphibolous answered 17/9, 2018 at 2:51 Comment(1)
Support for attr() except in content, is really scarce. You might check caniuse.com for it. :root and css-variables support is better, but still not yet so wide spread since support for it is pretty new. (also check support for it at caniuse.com)Accumbent
G
0

 $('.span').attr('data-txt', 'foo');
        $('.span').click(function () {
         $(this).attr('data-txt',"any other text");
        })
.span{
}
.span:after{ 
  content: attr(data-txt);
 }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class='span'></div>
Gadid answered 28/6, 2017 at 11:42 Comment(0)
S
0

The first time I'm not reading all given answers before giving my own, so I hope this doesn't bite me in the ...

In my case this was needed for icons attached to a, div and button elements, which worked a bit differently than the <i class="icon-class"></i> since, there was no icon-class class on them. Adding class="icon-class" broke styling though.

Instead I added a data-icon attribute to them with the value of what was supposed to be in element::before { content: "HERE" } and then this fairly simple JavaScript took care of the rest.

    {
        const fakeIcons = document.querySelectorAll('[data-icon]')

        for (const iconElement of fakeIcons) {

            const fakeClass = 'fake-' + Array.from(Array(20), () => Math.floor(Math.random() * 36).toString(36)).join('')
            const beforeContent = iconElement.getAttribute('data-icon')

            iconElement.classList.add(fakeClass)

            const style = document.createElement('style')
            style.type = 'text/css'
            style.innerHTML = `

                .${fakeClass}::before {
                    content: "${beforeContent}" !important;
                }

            `
            document.getElementsByTagName('head')[0].appendChild(style)
        }
    }

Code explained:

  • select all elements with the designated attribute (data-icon)
  • loop through them
  • randomly generate a class name starting with fake-, followed by a random alphanumeric string
  • get data-icon attribute's value
  • add randomly generated class to the element
  • create style for ::before pseudo-element setting content to previously fetched value
  • add styling to the end of <head> HTML element
Suppositious answered 3/8, 2021 at 6:29 Comment(0)
D
-1

I have something different stuff for you which is easy and effective.

    <style> 
    .case-after:after { // set your properties here like eg: 
        color:#3fd309 !important; 
     } 
     .case-before:before { // set your properties here like eg: 
        color:#151715 !important; 
     }
 </style>
  // case for after
    $('#button-id').on('click', function() {
        $(".target-div").toggleClass('case-after');
    });

     // case for before
    $('#button-id').on('click', function() {
        $(".target-div").toggleClass('case-before');
    });
Determine answered 12/11, 2019 at 9:32 Comment(0)
D
-1

Just set the pseudo before or after to have a style of inherit, then set the parent style with javascript.

So, for example I want to change the color style of the :before, then i set the:

.my-style::before{
color: inherit;
}

Then I just change the color style on the .my-style element with javascript:

document.querySelector(".my-style").style.color = red;

job done, super simple

Descartes answered 5/10, 2021 at 14:13 Comment(2)
hmm a perfectly acceptable, simple answer downvoted for no reason with no comment. Is this site slowly degrading???Descartes
What would happen, if you wanted the "before" text in red, but the main text in black?Suppositious
S
-1

If it is ok for you to write in the innerHTML. This is what helped in my usecase.

#setNotValidStyle(input) {
  input.style.border = "2px solid #d50000"
  input.style.borderRadius = "3px"
  input.innerHTML = ""
  input.innerHTML = `
    <style id="after">
      .${this.className}:after {
        content: "x";
        font-family: sans-serif;
        position: absolute;
        color: #d50000;
        padding-left: 8px;
       }
     </style>
   `
   return input
}
Stand answered 14/2, 2023 at 22:59 Comment(0)
I
-1
function getRuleWithSelector(selector) {
  var numSheets = document.styleSheets.length,
    numRules,
    sheetIndex,
    ruleIndex;
  // Search through the style sheets.
  for (sheetIndex = 0; sheetIndex < numSheets; sheetIndex += 1) {
    numRules = document.styleSheets[sheetIndex].cssRules.length;
    for (ruleIndex = 0; ruleIndex < numRules; ruleIndex += 1) {
      if (document.styleSheets[sheetIndex].cssRules[ruleIndex].selectorText == selector) {
        return document.styleSheets[sheetIndex].cssRules[ruleIndex];
      }
    }
  }
  // If we get this far, then the rule doesn't exist.
  // So the return value is undefined.
}

Use this function to select the css element like query selector then simply change it's style property, like

getRuleWithSelector('.navmenu ul li::after').style.top="15%" 

but to use this function properly your code must have imported the css style sheets in your DOM like this syntax

<link rel="stylesheet" href="css/custom.css">

and also it must come from your server if the stylesheet comes from another server it should throw an Security Error.

Refer to this post document.styleSheets does not detect all stylesheets, but most of the cases it will work.

Iphagenia answered 9/4, 2023 at 5:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.