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