86

I have a page that has an image in it and I styled it using :before CSS selectors.
The image is dynamic so it hasn't a fixed width; So I need to set :before rule's width dynamically.
I want do it in client side using JQuery.
Assume this:

.column:before{
    width: 300px;
    float: left;
    content: "";
    height: 430px;
}

.column{
    width: 500px;
    float: right;
    padding: 5px;
    overflow: hidden;
    text-align: justify;
}

How Can I only change the width property of class with :before selector (and not one without it) using JQuery?

alex
  • 479,566
  • 201
  • 878
  • 984
Ariyan
  • 14,760
  • 31
  • 112
  • 175
  • http://stackoverflow.com/questions/5041494/manipulating-css-before-and-after-pseudo-elements-using-jquery – The GiG Apr 08 '12 at 08:29
  • Can you tell the us the effect, you are trying to achieve? There may be a better way to do what you are tyring. – Starx Apr 08 '12 at 08:38
  • It's not duplicate @temani-afif – PHP Worm... Nov 04 '20 at 01:03
  • @PHPWorm... and how it's not a duplicate? – Temani Afif Nov 04 '20 at 01:10
  • @TemaniAfif In the duplicate question, it shows how to change content, not the width of ::after or ::before and in addition to this width of after and before can not be changed by the answer given in that question – PHP Worm... Nov 10 '20 at 17:04
  • @PHPWorm... read *all* the duplicate please. I can clearly understand from you comment that you didn't bother to read *all* the asnwers there. – Temani Afif Nov 10 '20 at 18:44

7 Answers7

114

I don't think there's a jQuery-way to directly access the pseudoclass' rules, but you could always append a new style element to the document's head like:

$('head').append('<style>.column:before{width:800px !important;}</style>');

See a live demo here

I also remember having seen a plugin that tackles this issue once but I couldn't find it on first googling unfortunately.

m90
  • 11,434
  • 13
  • 62
  • 112
  • 2
    One thing worth noting, though your example uses "one" so that there aren't multiple ` – dev_row Apr 17 '13 at 16:16
  • 1
    Here's a way to give each iteration its own class: http://jsfiddle.net/TB824/ – Heraldmonkey Dec 19 '13 at 16:08
  • Here's what I did based on this answer: http://jsfiddle.net/p1yc5bsw/ By using the text() method you can reuse the same element without destroying it each time. – Tristan Shelton Jan 28 '15 at 04:29
  • 1
    This is part thank you (because this solution is clever) but I also wanted to make it explicitly clear to anyone looking that ` – crowhill May 06 '15 at 04:24
  • Just want to note. Also used `append` but without `!important`. Worked in FF, but did not work in Chrome, Edge. So must not forget to use `!important` – Andris Aug 10 '18 at 15:47
  • Been almost 8 years since this answer was posted and this is still the best and most simple one I have seen on stack overflow. Kudos mate! – Caladin00 Feb 26 '20 at 13:55
63

Pseudo elements are part of the shadow DOM and can not be modified (but can have their values queried).

However, sometimes you can get around that by using classes, for example.

jQuery

$('#element').addClass('some-class');

CSS

.some-class:before {
    /* change your properties here */
}

This may not be suitable for your query, but it does demonstrate you can achieve this pattern sometimes.

To get a pseudo element's value, try some code like...

var pseudoElementContent = window.getComputedStyle($('#element')[0], ':before')
  .getPropertyValue('content')
Community
  • 1
  • 1
alex
  • 479,566
  • 201
  • 878
  • 984
28

Pseudo-elements are not part of the DOM, so they can't be manipulated using jQuery or Javascript.

But as pointed out in the accepted answer, you can use the JS to append a style block which ends of styling the pseudo-elements.

Starx
  • 77,474
  • 47
  • 185
  • 261
  • I didn't vote, but the elements can be styled from JS in the sense of creating more applicable style rules (as per the accepted answer), and surely styling is a form of manipulation... – nnnnnn Apr 12 '17 at 05:38
  • @nnnnnn Ok, but it's not doing the manipulation by itself, it's doing it by adding style rules which the browser reads and thus manipulation. Weird loophole though. But I agree that styling is a form of manipulation. – Starx Apr 12 '17 at 08:30
22

The answer should be Jain. You can not select an element via pseudo-selector, but you can add a new rule to your stylesheet with insertRule.

I made something that should work for you:

var addRule = function(sheet, selector, styles) {
    if (sheet.insertRule) return sheet.insertRule(selector + " {" + styles + "}", sheet.cssRules.length);
    if (sheet.addRule) return sheet.addRule(selector, styles);
};

addRule(document.styleSheets[0], "body:before", "content: 'foo'");

http://fiddle.jshell.net/MDyxg/1/

To be super-cool (and to answer the question really) I rolled it out again and wrapped this in a jQuery-plugin (however, jquery is still not required!):

/*!
 * jquery.addrule.js 0.0.1 - https://gist.github.com/yckart/5563717/
 * Add css-rules to an existing stylesheet.
 *
 * @see http://stackoverflow.com/a/16507264/1250044
 *
 * Copyright (c) 2013 Yannick Albert (http://yckart.com)
 * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php).
 * 2013/05/12
 **/

(function ($) {

    window.addRule = function (selector, styles, sheet) {

        styles = (function (styles) {
            if (typeof styles === "string") return styles;
            var clone = "";
            for (var p in styles) {
                if (styles.hasOwnProperty(p)) {
                    var val = styles[p];
                    p = p.replace(/([A-Z])/g, "-$1").toLowerCase(); // convert to dash-case
                    clone += p + ":" + (p === "content" ? '"' + val + '"' : val) + "; ";
                }
            }
            return clone;
        }(styles));
        sheet = sheet || document.styleSheets[document.styleSheets.length - 1];

        if (sheet.insertRule) sheet.insertRule(selector + " {" + styles + "}", sheet.cssRules.length);
        else if (sheet.addRule) sheet.addRule(selector, styles);

        return this;

    };

    if ($) $.fn.addRule = function (styles, sheet) {
        addRule(this.selector, styles, sheet);
        return this;
    };

}(window.jQuery));

The usage is quite simple:

$("body:after").addRule({
    content: "foo",
    color: "red",
    fontSize: "32px"
});

// or without jquery
addRule("body:after", {
    content: "foo",
    color: "red",
    fontSize: "32px"
});

https://gist.github.com/yckart/5563717

yckart
  • 32,460
  • 9
  • 122
  • 129
  • Very cool. I [tweaked it](https://gist.github.com/corysimmons/10949352) a bit to get it to work for me, but it doesn't seem to work in Firefox. – corysimmons Apr 17 '14 at 06:17
  • @CorySimmons Looks great, I've added a comment to my gist :) – yckart Apr 17 '14 at 09:32
  • Turn it into a repo and I'll work on it with you. Right now it doesn't work in FF or old IE but it's fixable. – corysimmons Apr 18 '14 at 06:06
  • This may raise cross-domain security issues in some browsers. `sheet.cssRules`for example will be `null` in Chrome. – MichaelKaeser Oct 06 '14 at 21:45
  • it works. But it is important to select a correct index of stylesheets. i used ```console.log(document.styleSheets);``` to find which is nedded – Vlad Feb 11 '17 at 11:22
5

One option is to use an attribute on the image, and modify that using jQuery. Then take that value in CSS:

HTML (note I'm assuming .column is a div but it could be anything):

<div class="column" bf-width=100 >
    <img src="..." />
</div>

jQuery:

// General use:
$('.column').attr('bf-width', 100);
// With your image, along the lines of:
$('.column').attr('bf-width', $('img').width());

And then in order to use that value in CSS:

.column:before {
    content: attr(data-content) 'px';
    /* ... */
}

This will grab the attribute value from .column, and apply it on the before.

Sources: CSS attr (note the examples with before), jQuery attr.

Avatar
  • 14,622
  • 9
  • 119
  • 198
Selfish
  • 6,023
  • 4
  • 44
  • 63
  • 1
    This sounds very nice, but does not work for me. The MDN reference you mention sais "The attr() function can be used with any CSS property, but support for properties other than content is experimental." – Philipp Zedler Feb 06 '16 at 15:53
  • This is the best answer in the original question: https://stackoverflow.com/a/5734583/1066234 – Avatar Oct 26 '22 at 11:57
4

You may try to inherit property from the base class:

var width = 2;
var interval = setInterval(function () {
    var element = document.getElementById('box');
    width += 0.0625;
    element.style.width = width + 'em';
    if (width >= 7) clearInterval(interval);
}, 50);
.box {
    /* Set property */
    width:4em;
    height:2em;
    background-color:#d42;
    position:relative;
}
.box:after {
    /* Inherit property */
    width:inherit;
    content:"";
    height:1em;
    background-color:#2b4;
    position:absolute;
    top:100%;
}
<div id="box" class="box"></div>
Alexander Shutau
  • 2,660
  • 22
  • 32
3

As Boltclock states in his answer to Selecting and manipulating CSS pseudo-elements such as ::before and ::after using jQuery

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, and thus you can't select and manipulate them with jQuery.

Might just be best to set the style with jQuery instead of using the pseudo CSS selector.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Dean_Wilson
  • 190
  • 13