On targeting pseudo-elements with JavaScript/jQuery

Pseudo-elements are pretty nice things you can inject into HTML elements via CSS.

12345678910
#container { 
    position: relative;
}
#container::before {
    position: absolute;
    top: 0;
    left: 0;
    display: block;
    content: "I am a block element in the top-left corner of #container and I don't exist in the markup";
}

They are very useful for adding presentational output to your DOM tree without needing to pollute the markup. But they are a bit of a pain because they don't really exist in the DOM, so accessing them programmatically with JavaScript is hard, or I think, impossible in the general case.

But depending on what you want to do, you might be able to achieve the result you want by being a bit clever...

Usually you want to target this element because you intend to manipulate its CSS. You can still manipulate its CSS! You just have to take the indirect and less commonly trodden route of using JS to create a stylesheet and writing raw rules into it. Example:

1234567
$('head').append(document.createElement('style'));
var myStyleSheet = document.styleSheets[document.styleSheets.length-1];

var myRule = "#container::before { content: \"Let's change my text\"; }";
if (myStyleSheet.insertRule) {
    myStyleSheet.insertRule(myRule, myStyleSheet.cssRules.length);
}

What we've done in the code above is to create a dedicated stylesheet for this and then injected a rule which should override the previous rule (assuming selector specifity is equal, but let's not get into that). This is obviously not as neat as simply using jQuery.css(), but we don't have too many options, so let's focus on managing the situation we've inherited.

Cool story bro, but how do I scale this?

Exactly what strategy you use to lay your code out depends on your exact problem. If you only need to consider changes to one element, just keep it simple.

But if you need to handle multiple elements individually you have to manage problems selecting them and targeting them individually. If they don't already have an ID set, you'll have to create IDs on them and use that in the rule's selector. The second parameter of insertRule is an index. We can use this to help us out; we can iterate over each of our elements initially and assign it an index (use $.data()), insert a no-op rule at that index to force it to exist, and then make sure that element may only write to that index. After doing so, we'll still have to remove the old rule which will now be in index+1, which we can do with myStyleSheet.deleteRule(index+1).

I've made a fiddle to show roughly how it works. It by no means handles all feasible cases but should give you an idea how to lay things out.

Talk is cheap

Leave a comment:

HTML is not valid. Use:
[url=http://www.google.com]Google[/url] [b]bold[/b] [i]italics[/i] [u]underline[/u] [code]code[/code]