8

EDIT 2015-10-07 1624 CST

This question has been tagged as a possible duplicate - the reason I posted it is that none of the answers to the other question provided all the information I wanted and I want a simple and direct way to do it. I can take care of controlling the order of stylesheets and rules so that I am referencing the correct rule. I also wanted feedback as to the feasibility of this method as regards it breaking in the future.

Look at the comments I posted below for additional reasons why I didn't accept the answers to the possible duplicate questions.

** original question follows **

I searched for a question like this and found some that address part of the question but not both retrieving and changing CSS values for pseudo elements like ::before and ::after.

I have searched the web with Google before coming here and basically what I found is that there is no ideal way to do this.

I found a way that works in FF 40, IE 9, and Chrome 45.0.2454.101 m but I'm wondering if I'm overlooking something that may cause my method to break in some instances.

The answers I've seen, here and other places on the web, about accessing or changing CSS values for pseudo elements, say that you can't directly access these items because they are not "part of the DOM" and "outside the DOM"

They say about the only way to change them is to create a new rule and appending it to the existing rules to override the coded value.

Here is a snippet that demonstrates the method:

function changeColor () { // Flip psedo element's background color
 var newColor,
  currentColor;
 
    // Get the current color
 currentColor= document.styleSheets[0].cssRules[0].style.backgroundColor;  
 
    // flip the color
 newColor = (currentColor== "red") ? "aqua" : "red";         
 
     // Change the color
    document.styleSheets[0].cssRules[0].style.backgroundColor = newColor;     
    
    // put color in top message
    document.getElementById("colorIs").innerHTML = newColor;
  
    // display colors  
 document.getElementById("displayColors").innerHTML = 
   "Pevious color was " + currentColor + 
            ", changed to " + newColor + ".";  
            
    // Change background of button (not needed but thought I'd throw it in)
    document.getElementById("changeButton").style.backgroundColor = newColor;                      
}
#testDiv::before {
      background-color: aqua;
   content: "psedo element ";
  }

#changeButton {
 background-color: aqua;
}
<div id="testDiv">
  This divsion has an pseudo ::before element whose background color is
  <span id="colorIs">
  aqua
 </span>
  <br>
  <br> Click "Display and Flip Color" to display the colors
  <br> and flip the color from aqua and red and vice versa.
</div>
<br>
<form method="post">
  <input id="changeButton" name="change" type="button" value="Display and Flip Color" onclick="changeColor();">
</form>
<br>
<div id="displayColors">
</div>

I realize that this depends upon my knowing the order of style sheets and rules within them, but I can control that.

These seems to go against the answers I've seen which say the CSS items for pseudo elements are not part of, and therefore not accessible, through the DOM.

Is this method possible because of browser or DOM changes that occurred after the answers I read were posted?

What is the likelihood that this method might break in the future?

And would those with different version of the various browsers please try the snippet and let me know if it works or not?

Bob


EDIT 2015-10-08 1352 CST

I have modified my method of accessing styles of pseudo elements to be able to directly reference the style sheet in question, regardless of the order in which it is defined.

I would change the snippet but I don't see a way to give the css "stylesheet" an id.

Instead I'll tell how I would modify it.

1) Separate the CSS that defines the elements being used and put it in a separate file.

2) Code id= on the <link tag referencing the CSS file. In this case I would use id="colorFlipFlop"

3) Change the JavaScript to reference or change a style from this:

currentColor = document.styleSheets[0].cssRules[0].style.backgroundColor;

document.styleSheets[0].cssRules[0].style.backgroundColor = newColor;

To:

var beforeIndex = 0; // give a name to the index, in cssRules, of the rule we want to get and change.

var styleSheetObject = document.getElementById("colorFlipFlop"); // get a reference to the stylesheet object

currentColor = styleSheetObject.sheet.cssRules[beforeIndex].style.backgroundColor;   // get current pseudo element background color

styleSheetObject.sheet.cssRules[beforeIndex].style.backgroundColor = newColor; // set new background color

I would fully document all of this, in the CSS and Javascript, and in the HTML if I deemed it necessary, with as many comments as I thought was necessary to explain what I am doing, how I am doing it, and why I am doing it the way I am doing it - I call that the WHW of commenting.

I feel this makes the functionality more manageable and more bullet proof.

You no longer have to now the index of the stylesheet object, everything is neatly separated from everything else on the page, and it still provides a direct way to access and change styles of pseudo elements without creating and appending new rules.

Before I post this edit I will make up a file containing the CSS, JavaScript, and HTML to achieve what the snippet does to show the new method. I will put everything in one file just to make it easier to create and FTP to the site. In real world code, I'd use separate CSS, JavaScript, and HTML files.

It will be at http://thetesting.site/flipFlopColor.html

So - What do you think?

gilly3
  • 87,962
  • 25
  • 144
  • 176
SimonT
  • 486
  • 1
  • 4
  • 16
  • another simple way is append a style tag and set the text as a proper css rule – charlietfl Oct 06 '15 at 23:10
  • I think this is a well posed and researched question, especially for a newcomer. Deserving of upvotes. Although it is probably a lot easier to define the CSS beforehand and simply add a class to the parent. Which is likely also what charlietfl is trying to convey. Here's a small example : http://tinyurl.com/pr6sud8. – Shikkediel Oct 07 '15 at 05:52
  • Possible duplicate of [Changing CSS pseudo-element styles via JavaScript](http://stackoverflow.com/questions/4481485/changing-css-pseudo-element-styles-via-javascript) – Abhitalks Oct 07 '15 at 06:34
  • I must disagree there, OP is not just looking for a simple fix but trying to understand the mechanism. – Shikkediel Oct 07 '15 at 17:02
  • One reason I did not go the append rule route is that this change may need to be done more than once on a web page and why mess with creating rules and values and then appending to the style.. etc,? also, there would no way that I could pre-code the new values since they will vary all over the place. @Shikkediel thank you comments on this matter. – SimonT Oct 07 '15 at 23:00
  • As to duplicate question - one of the answers of that question said : "You can't apply styles to psuedo-elements in JavaScript." Well, I had found a way so I was a bit hesitant to accept the answers to that question and also because if was post in 2010 and things change a lot in five years - particularly when it concerns JS, DOM, HTML, and CSS. Also,there was no mention of the method I had found and I simply thought it best to make up a snippet and post the question. The possible duplicate is at http://stackoverflow.com/questions/4481485/changing-css-pseudo-element-styles-via-javascript – SimonT Oct 07 '15 at 23:39
  • I forgot to add - if it gets to hard to keep the stylesheet as the first and the rule I want as the first rule in the sheet, I'll fall back to looping through the stylesheet and then ooping through cssRules to find the one I need. I've begun putting id= on – SimonT Oct 07 '15 at 23:46
  • The accepted answer there recommends to *never ever* use `cssRules` because it would be wrong. I think this is a rather silly statement, it is now a widely supported feature and would only fail in IE8-. And it really is the only way to directly address pseudo elements. Or rather just their style, because as a DOM element itself is not selected in any way. I am not sure if it is very clear to follow but I've juggled with it myself as well and made this example (it's using jQuery though) : http://codepen.io/Shikkediel/pen/QwKMBQ. There I also filter the sheet by applying a `regex` to the rules. – Shikkediel Oct 07 '15 at 23:52
  • @Shikkediel - You can modify `cssRules` in IE8 and even earlier. You have to write some cross-browser code because the API changed, but it worked in IE6 and maybe even IE4. – gilly3 Oct 07 '15 at 23:57
  • Sure but as you have posted, it's obscure enough as is. Unless deep browser support is absolutely necessary that might better be avoided. Interesting subject in any case. – Shikkediel Oct 08 '15 at 00:00
  • Why do you want to do this? What is the high-level use case? It is extremely rare for munging stylesheets to be the right solution to a problem. There is almost always a cleaner way. –  Oct 09 '15 at 04:53

1 Answers1

2

Manipulating the CSSRule instead of the DOM element is an obscure but perfectly valid (and standardized) way of changing an element's style. It's obscure because it is difficult, requiring a nested loop going through all the rules in all the stylesheets to find the rule you want to change. And it's obscure also because it's not super valuable - you can usually accomplish the same thing by just accessing the DOM element's style property.

But, with pseudo-elements, there is no DOM element. The pseudo-element is a product of the style rule, so the only way to manipulate the pseudo-element is through the style rule. People recommend adding style rules because that is easier than finding the style rule and editing it. But finding and editing it is perfectly valid.

You can have the best of both worlds by adding a style rule once, and then keeping a reference to the rule and making subsequent edits to that same rule.

gilly3
  • 87,962
  • 25
  • 144
  • 176
  • I agree that locating the rule you want to change can complicate things. The scheme would break if you forgot and added rule above one you expected to be at a specific spot in the style-sheet or placed another style sheet before the one you are targeting. But, I really like my approach, not because I worked it out, but because it is and looks so straight forward. I am exploring to see if there is a way to record, in some manner, ideally in the style-sheet itself, the index values of the rules I want to change. I'll update my question or post additional comments about my findings. – SimonT Oct 08 '15 at 17:40
  • @SimonT - Personally, I like your approach a lot, and have recommended it in the past. But, beware of locating rules by hard-coded index. In [this comment thread](http://stackoverflow.com/questions/6115325/change-css-rule-in-class-using-jquery/7014965#comment32329292_7014965) a user on a different OS than me had his stylesheet or rule at a different index than I expected. It's best to rely on finding the rule once (or creating it dynamically) and then keeping a reference to that rule, rather than hard-coding the expected index. – gilly3 Oct 08 '15 at 18:30
  • That a look at the edit at the bottom of my question. I found a method of referencing the correct stylesheet without knowing the order in which it was defined, and thus, its index in the stylesheet object. Let me know what you think of the modified method. – SimonT Oct 08 '15 at 20:51
  • If I am not mistaken, you can give the stylesheet in question an `id` and select it reliably through that. I have not gone up to the point of experimentation myself where I would try to distinguish an external sheet from style that is placed in the head section. For that a local demo is needed. When I tried on Codepen, I discovered there are CORS restrictions on accessing a stylesheet that isn't on the same domain. – Shikkediel Oct 08 '15 at 23:21
  • @Shikkediel That is exactly what I did, read my edit at the bottom of my question. – SimonT Oct 09 '15 at 02:23
  • Indeed, guess I missed that. Looks like a fine approach. – Shikkediel Oct 09 '15 at 02:26
  • @Shikkediel No problem with your missing the edit - I've missed edits on questions myself. As to the issue, I just wish that the CSS provided something akin to id= for sheets (the sheet, not the – SimonT Oct 10 '15 at 22:45