Categories
css javascript jquery jquery-selectors pseudo-element

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

1088

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?

5

  • 4

    I made something that should work for you: gist.github.com/yckart/5563717

    – yckart

    May 12, 2013 at 14:20

  • 2

    I hate such comments on stackoverflow that I’m going to write (where commenter asks why don’t you do it some complete opposite way?), but: at some point one should realize code-design issue and replace that pseudo element with a span or something. I guess you know what I’m pointing at.

    – zsitro

    May 11, 2016 at 10:53


  • 1

    Accepted answer is excellent. Though if you are trying to achieve any of the following points, the answer wont work: 1. change style of :before by JS. 2. change content of :before by JS Please see my answer for that if you need it.

    Nov 14, 2016 at 13:52


  • @Learner Now there is an answer tfor all of these: stackoverflow.com/a/49618941/8620333

    May 1, 2019 at 14:36

765

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

12

  • 2

    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…

    Aug 19, 2011 at 19:28

  • 102

    +1 for attr(), too bad i wasn’t able to use it with other properties than content. Demo

    Jan 20, 2012 at 1:05

  • 29

    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.

    – BoltClock

    Feb 8, 2012 at 15:49


  • 10

    Updated link for Attribute References: w3.org/TR/css3-values/#attr-notation

    – user166560

    Mar 23, 2013 at 15:57

  • 5

535

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

7

  • 2

    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?

    Jun 19, 2015 at 17:05

  • @user2101068 You should post that as a new question. I’d have to see all the code you’re using.

    Jun 19, 2015 at 17:41


  • 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?

    Jun 19, 2015 at 18:20


  • 1

    @user2101068 Don’t use the hex string; instead, copy and paste the actual Unicode character into the HTML attribute. jsfiddle.net/mblase75/Lcsjkc5y

    Jun 19, 2015 at 19:06


  • 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)

    – Picard

    Sep 2, 2016 at 10:58

164

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

0